Go에서 단위 테스트와 통합 테스트 분리


98

GoLang (testify)에서 단위 테스트와 통합 테스트를 분리하는 모범 사례가 있습니까? 단위 테스트 (외부 리소스에 의존하지 않고 따라서 매우 빠르게 실행 됨)와 통합 테스트 (외부 리소스에 의존하여 느리게 실행 됨)가 혼합되어 있습니다. 그래서 내가 말할 때 통합 테스트를 포함할지 여부를 제어 할 수 있기를 원합니다 go test.

가장 간단한 기술은 main에 -integrate 플래그를 정의하는 것 같습니다.

var runIntegrationTests = flag.Bool("integration", false
    , "Run the integration tests (in addition to the unit tests)")

그런 다음 모든 통합 테스트의 맨 위에 if 문을 추가하려면 :

if !*runIntegrationTests {
    this.T().Skip("To run this test, use: go test -integration")
}

이것이 내가 할 수있는 최선인가? 나는 이름 지정 규칙이나 나를 위해 이것을 수행하는 무언가가 있는지 확인하기 위해 증언 문서를 검색했지만 아무것도 찾지 못했습니다. 내가 뭔가를 놓치고 있습니까?


2
나는 stdlib가 -short를 사용하여 네트워크를 공격하는 테스트를 비활성화한다고 생각합니다 (및 기타 장기 실행 항목도). 그렇지 않으면 솔루션이 괜찮아 보입니다.
Volker

-short는 사용자 정의 빌드 플래그와 마찬가지로 좋은 옵션이지만 플래그가 main에있을 필요는 없습니다. var를 var integration = flag.Bool("integration", true, "Enable integration testing.")함수 외부 로 정의하면 변수가 패키지 범위에 표시되고 플래그가 제대로 작동합니다
Atifm

답변:


156

@ Ainar-G는 테스트를 분리하기 위해 몇 가지 훌륭한 패턴을 제안합니다.

SoundCloud의이 Go 사례 모음은 빌드 태그 ( 빌드 패키지의 'Build Constraints'섹션에 설명 됨 )를 사용하여 실행할 테스트를 선택 하도록 권장 합니다.

integration_test.go를 작성하고 통합 빌드 태그를 지정합니다. 서비스 주소 및 연결 문자열과 같은 항목에 대한 (전역) 플래그를 정의하고 테스트에서 사용합니다.

// +build integration

var fooAddr = flag.String(...)

func TestToo(t *testing.T) {
    f, err := foo.Connect(*fooAddr)
    // ...
}

go test는 go build와 마찬가지로 빌드 태그를 사용하므로 go test -tags=integration. 또한 flag.Parse를 호출하는 패키지 main을 합성하므로 선언되고 표시되는 모든 플래그가 처리되어 테스트에서 사용할 수 있습니다.

유사한 옵션으로 빌드 조건을 사용하여 기본적으로 통합 테스트를 실행 한 // +build !unit다음을 실행하여 요청시 비활성화 할 수도 있습니다 go test -tags=unit.

@adamc 코멘트 :

빌드 태그를 사용하려는 다른 사용자의 경우 // +build test주석이 파일의 첫 번째 줄이고 주석 뒤에 빈 줄을 포함하는 것이 중요합니다. 그렇지 않으면 -tags명령이 지시문을 무시합니다.

또한 빌드 주석에 사용 된 태그에는 밑줄이 허용되지만 대시를 사용할 수 없습니다. 예를 들어 // +build unit-tests는 작동하지 않는 반면 // +build unit_tests윌은 작동하지 않습니다 .


1
나는 이것을 얼마 동안 사용해 왔으며 지금까지 가장 논리적이고 간단한 접근 방식입니다.
Ory Band

1
당신은 동일한 패키지에서 단위 테스트가있는 경우, 당신은 설정 필요 // + build unit단위 테스트에서 실행 테스트를 위해 -tag 기기를 사용
LeoCBS

2
@ Tyler.z.yang 태그 사용 중단에 대한 링크 나 자세한 정보를 제공 할 수 있습니까? 그런 정보를 찾지 못했습니다. 답변에 설명 된 방식으로 go1.8과 함께 태그를 사용하고 테스트에서 유형과 함수를 조롱합니다. 내가 생각하는 인터페이스에 대한 좋은 대안입니다.
Alexander I.Grafov

2
빌드 태그를 사용하려는 다른 사람의 경우 // +build테스트 주석이 파일의 첫 번째 줄이고 주석 뒤에 빈 줄을 포함하는 것이 중요합니다. 그렇지 않으면 -tags명령이 지시문을 무시합니다. 또한 빌드 주석에 사용 된 태그에는 밑줄이 허용되지만 대시를 사용할 수 없습니다. 예를 들어, // +build unit-tests하지 않습니다 작업 반면, // +build unit_tests의지
adamc

6
와일드 카드를 처리하는 방법은 무엇입니까? go test -tags=integration ./...나던 작품은,이 태그를 무시
에리카 Dsouza

54

@ Ainar-G의 탁월한 답변에 대한 내 의견에 대해 자세히 설명하기 위해 지난 1 년 동안 나는 두 세계의 장점을 모두 달성하기 위해 명명 규칙 -short과 의 조합을 사용해 왔습니다 Integration.

단위 및 통합은 동일한 파일에서 조화를 테스트합니다.

빌드 플래그는 이전에 여러 파일 (이 저를 강요 services_test.go, services_integration_test.go등).

대신 처음 두 개가 단위 테스트이고 마지막에 통합 테스트가있는 아래의 예를 사용하세요.

package services

import "testing"

func TestServiceFunc(t *testing.T) {
    t.Parallel()
    ...
}

func TestInvalidServiceFunc3(t *testing.T) {
    t.Parallel()
    ...
}

func TestPostgresVersionIntegration(t *testing.T) {
    if testing.Short() {
        t.Skip("skipping integration test")
    }
    ...
}

마지막 테스트에는 다음과 같은 규칙이 있습니다.

  1. 사용 Integration테스트 이름.
  2. -short플래그 지시문에서 실행 중인지 확인합니다 .

기본적으로 사양은 다음과 같습니다. "모든 테스트를 정상적으로 작성하십시오. 장기간 실행되는 테스트이거나 통합 테스트 인 경우이 명명 규칙을 따르고 -short동료에게 좋은지 확인하십시오 ."

단위 테스트 만 실행 :

go test -v -short

이것은 다음과 같은 멋진 메시지 세트를 제공합니다.

=== RUN   TestPostgresVersionIntegration
--- SKIP: TestPostgresVersionIntegration (0.00s)
        service_test.go:138: skipping integration test

통합 테스트 만 실행 :

go test -run Integration

통합 테스트 만 실행합니다. 프로덕션에서 연기 테스트 카나리아에 유용합니다.

분명히이 접근 방식의 단점 go test-short플래그 없이 누구나를 실행 하면 기본적으로 모든 테스트 (단위 및 통합 테스트)를 실행한다는 것입니다.

실제로 프로젝트가 단위 및 통합 테스트를 수행 할 수있을만큼 충분히 크면 Makefile간단한 지시문을 사용할 수 있는 곳을 사용하고있을 가능성이 큽니다 go test -short. 또는 README.md파일에 넣고 오늘이라고 부르세요.


3
단순 사랑
야곱 스탠리

패키지의 공개 부분에만 액세스하기 위해 이러한 테스트에 대해 별도의 패키지를 생성합니까? 아니면 모두 혼합?
Dr.eel

@ Dr.eel 글쎄, 그것은 대답의 OT입니다. 그러나 개인적으로 저는 두 가지를 모두 선호합니다. 테스트에 대해 다른 패키지 이름을 사용하여 import패키지 및 테스트를 수행 할 수 있으므로 API가 다른 사람에게 어떻게 보이는지 보여줍니다. 그런 다음 내부 테스트 패키지 이름으로 다루어야하는 나머지 로직에 대해 후속 조치를 취합니다.
eduncan911

@ eduncan911 답변 주셔서 감사합니다! 그래서 여기 package services에 통합 테스트 sute가 포함되어 있으므로 APIfo 패키지를 블랙 박스로 테스트하려면 package services_integration_test내부 구조로 작업 할 기회를주지 않는 다른 이름을 지정해야합니다 . 따라서 단위 테스트 (내부 액세스) 용 패키지의 이름은 package services. 그렇습니까?
Dr.eel

네, 맞습니다. 여기가 어떻게 깨끗한 예입니다 github.com/eduncan911/podcast (통지 100 %의 코드 커버리지, 예를 사용)
eduncan911

50

세 가지 가능한 해결책이 있습니다. 첫 번째는 단위 테스트에 짧은 모드 를 사용하는 것 입니다. 따라서 go test -short단위 테스트와 동일하지만 -short통합 테스트를 실행하기 위해 플래그 없이 사용합니다 . 표준 라이브러리는 단기 모드를 사용하여 장기 실행 테스트를 건너 뛰거나 더 간단한 데이터를 제공하여 더 빠르게 실행되도록합니다.

두 번째는 규칙을 사용하여 테스트 중 하나를 호출하는 것입니다 TestUnitFoo또는 TestIntegrationFoo다음 사용 -run테스트 플래그를 실행하는 테스트를 표시하기 위해. 따라서 go test -run 'Unit'단위 테스트 및 go test -run 'Integration'통합 테스트에 사용합니다.

세 번째 옵션은 환경 변수를 사용하고 os.Getenv. 그런 다음 go test단위 테스트 및 FOO_TEST_INTEGRATION=true go test통합 테스트에 단순 을 사용 합니다.

개인적으로 -short더 간단하고 표준 라이브러리에서 사용되는 솔루션 을 선호 하므로 장기 실행 테스트를 분리 / 단순화하는 사실상의 방법 인 것 같습니다. 그러나 -runos.Getenv솔루션은 더 많은 유연성을 제공합니다 (정규 표현식이와 관련되어 있으므로 더 많은주의가 필요합니다 -run).


1
Tester-GoIDE (Atom, Sublime 등)에 공통적 인 커뮤니티 테스트 실행기 (예 :)에는 -short플래그와 함께 실행할 수있는 기본 제공 옵션이 -coverage있습니다. 따라서 테스트 이름에 통합과 if testing.Short()해당 테스트 내의 검사를 함께 사용 합니다. 실행 : 그것은 나를 두 세계의 최고 가질 수 -short십오 내에서 명시 적으로 만 통합 테스트를 실행go test -run "Integration"
eduncan911

5

나는 최근에 같은 해결책을 찾으려고 노력했습니다. 내 기준은 다음과 같습니다.

  • 솔루션은 보편적이어야합니다.
  • 통합 테스트를위한 별도의 패키지 없음
  • 분리가 완료되어야합니다 (통합 테스트 실행할 수 있어야 함 ).
  • 통합 테스트를위한 특별한 명명 규칙 없음
  • 추가 도구없이 잘 작동해야합니다.

앞서 언급 한 솔루션 (사용자 지정 플래그, 사용자 지정 빌드 태그, 환경 변수)은 위의 모든 기준을 실제로 충족하지 못했기 때문에 약간의 파고 플레이 후이 솔루션을 찾았습니다.

package main

import (
    "flag"
    "regexp"
    "testing"
)

func TestIntegration(t *testing.T) {
    if m := flag.Lookup("test.run").Value.String(); m == "" || !regexp.MustCompile(m).MatchString(t.Name()) {
        t.Skip("skipping as execution was not requested explicitly using go test -run")
    }

    t.Parallel()

    t.Run("HelloWorld", testHelloWorld)
    t.Run("SayHello", testSayHello)
}

구현은 간단하고 최소화됩니다. 테스트에는 간단한 규칙이 필요하지만 오류가 덜 발생합니다. 추가 개선은 코드를 도우미 함수로 내보내는 것입니다.

용법

프로젝트의 모든 패키지에 대해서만 통합 테스트를 실행합니다.

go test -v ./... -run ^TestIntegration$

모든 테스트 실행 ( 정기 및 통합) :

go test -v ./... -run .\*

정기 테스트 만 실행 :

go test -v ./...

이 솔루션은 도구 없이도 잘 작동하지만 Makefile 또는 일부 별칭을 사용하면 사용자가 더 쉽게 사용할 수 있습니다. 또한 go 테스트 실행을 지원하는 모든 IDE에 쉽게 통합 할 수 있습니다.

전체 예제는 https://github.com/sagikazarmark/modern-go-application 에서 찾을 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.