Programing

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

crosscheck 2020. 10. 9. 08:50
반응형

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


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")
}

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


@ 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윌은 작동하지 않습니다 .


세 가지 가능한 해결책이 있습니다. 첫 번째는 단위 테스트에 짧은 모드 를 사용하는 것 입니다. 따라서 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).


@ 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

this provides you with a nice set of messages like:

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

Run Integration Tests only:

go test -run Integration

This runs only the integration tests. Useful for smoke testing canaries in production.

Obviously the downside to this approach is if anyone runs go test, without the -short flag, it will default to run all tests - unit and integration tests.

In reality, if your project is large enough to have unit and integration tests, then you most likely are using a Makefile where you can have simple directives to use go test -short in it. Or, just put it in your README.md file and call it the day.


I was trying to find a solution for the same recently. These were my criteria:

  • The solution must be universal
  • No separate package for integration tests
  • The separation should be complete (I should be able to run integration tests only)
  • No special naming convention for integration tests
  • It should work well without additional tooling

The aforementioned solutions (custom flag, custom build tag, environment variables) did not really satisfy all the above criteria, so after a little digging and playing I came up with this solution:

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)
}

The implementation is straightforward and minimal. Although it requires a simple convention for tests, but it's less error prone. Further improvement could be exporting the code to a helper function.

Usage

Run integration tests only across all packages in a project:

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

Run all tests (regular and integration):

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

Run only regular tests:

go test -v ./...

This solution works well without tooling, but a Makefile or some aliases can make it easier to user. It can also be easily integrated into any IDE that supports running go tests.

The full example can be found here: https://github.com/sagikazarmark/modern-go-application

참고URL : https://stackoverflow.com/questions/25965584/separating-unit-tests-and-integration-tests-in-go

반응형