기능 유형으로 테스트 가능한 이동

8491 단어 gotddtesting
go 코드에 대한 테스트 사례를 작성하는 데 도움이 되는 패턴은 Ports and Adapters 입니다. 이론이 있는 긴 블로그 게시물을 건너뛰고 이것이 일반적인 go 코드에서 어떻게 보이는지 알아볼 수 있습니다.

func TellEveryBobHeSmells() error {
    emailSvc := getEmailService()
    db := getDBConn()
    rows := db.Exec(`select bobs from db`)
    for row.Next() {
        var bobsEmail string
        err := rows.Scan(&bobsEmail)
        if err != nil // ...
        err = emailSvc.SendSpiceyEmail(bobsEmail)
        if err != nil // ...
    }
}


이 기능은 테스트하기에 매우 성가실 것입니다. 입력이 필요하지 않으며 2개의 외부 서비스에 도달합니다. 먼저 밥 목록을 얻고 이메일을 보낼 또 다른 목록을 가져옵니다. 이 코드를 더 테스트하기 쉽게 리팩토링할 수 있습니다.

type sendSpicyEmailFn = func(email string) error
type bobFinderFn = func() ([]string, error)

func tellEveryBobHeSmells(findBobs bobFinderFn, sendEmail sendSpicyEmailFn) error {
    bobs, err := findBobs()
    if err != nil // ...
    for _, email := bobs {
        err := sendEmail(email)
        if err != nil // ...
    }

    return nil
}


구현의 부작용을 추상화하기 위해 두 개의 "포트"를 만들었습니다. sendSpicyEmailFnbobFinderFn . 이제 테스트 케이스는 이러한 기능의 동작을 제어할 수 있습니다. 테스트 스위트에 데이터베이스를 제공하지 않고도 이 함수에 대한 단위 테스트를 작성하는 것을 상상할 수 있습니다.

// now we write a public wrapper function that fills in
// the adaptors so our callers dont have to care about ports/adaptors
func TellEveryBobHeSmells() error {
    // note we still passing in dependencies to the adaptors
    db := getDBConn()
    findBobs := defaultBobFinderFn(db)

    emailSvc := getEmailService()
    sendEmail := defaultSendSpicyEmailFn(emailSvc)

    return tellEveryBobHeSmells(findBobs, sendEmail)
}

// adaptor implementations
func defaultBobFinderFn(db *DB) bobFinderFn {
    return func() ([]string, error) {
        var bobs []string
        rows := db.Exec(`select bobs from db`)
        for row.Next() {
            var email string
            err := rows.Scan(&email)
            if err != nil // ...
            bobs = append(bobs, email)
        }

        return bobs, nil
    }
}

func defaultSendSpicyEmailFn(svc *EmailSvc) sendSpicyEmailFn {
    return func(email string) error{
        return emailSvc.SendSpiceyEmail(email)
    }
}


이제 기능을 비즈니스 로직과 외부 종속성으로 분리했으므로 테스트하기가 훨씬 쉽습니다. 일반적으로 tellEveryBobHeSmells 및 두 개의 어댑터 defaultBobFinderFndefaultSendSpicyEmailFn 에 대한 단위 테스트를 생성하지만 TellEveryBobHeSmells 에 대한 테스트 사례는 건너뜁니다.

좋은 웹페이지 즐겨찾기