Go에서 테스트 패키지를 사용하여 테스트 설정을 수행하려면 어떻게해야합니까?


답변:


159

Go 1.4부터는 설정 / 해체를 구현할 수 있습니다 (각 테스트 전후에 함수를 복사 할 필요 없음). 설명서는 여기 기본 섹션 에 요약되어 있습니다 .

TestMain은 메인 고 루틴에서 실행되며 m.Run 호출과 관련하여 필요한 모든 설정 및 해체를 수행 할 수 있습니다. 그런 다음 m.Run의 결과로 os.Exit를 호출해야합니다.

테스트에 함수가 포함되어 있으면 테스트 func TestMain(m *testing.M)를 실행하는 대신이 함수가 호출 된다는 것을 알아내는 데 시간이 좀 걸렸습니다 . 이 함수에서 테스트 실행 방법을 정의 할 수 있습니다. 예를 들어 전역 설정 및 해체를 구현할 수 있습니다.

func TestMain(m *testing.M) {
    setup()
    code := m.Run() 
    shutdown()
    os.Exit(code)
}

여기에서 몇 가지 다른 예 를 찾을 수 있습니다 .

최신 릴리스에서 Go의 테스트 프레임 워크에 추가 된 TestMain 기능은 여러 테스트 사용 사례를위한 간단한 솔루션입니다. TestMain은 설정 및 종료를 수행하고, 테스트 환경을 제어하고, 자식 프로세스에서 다른 코드를 실행하거나, 테스트 코드에서 유출 된 리소스를 확인하기위한 전역 후크를 제공합니다. 대부분의 패키지에는 TestMain이 필요하지 않지만 필요할 때 추가 할 수 있습니다.


17
TestMain한 번 패키지에 들어 있으므로 그다지 유용하지 않습니다. 나는 찾을 하위 검사가 더 복잡한 목적을 위해 더 낫다.
Inanc Gumus 2017-06-14

3
전역 변수를 사용하지 않고 설정 함수에서 테스트로 컨텍스트를 어떻게 전달해야합니까? 예를 들어 mySetupFunction ()이 테스트를 수행 할 임시 디렉터리 (고유 한 임의 이름 사용)를 만드는 경우 테스트에서 디렉터리 이름을 어떻게 알 수 있습니까? 이 컨텍스트를 설정할 장소가 있어야합니다. ??
Lqueryvg

1
이것이 테스트를위한 전후 후크를 처리하는 공식적인 방법 인 것 같습니다. 공식 문서는 golang.org/pkg/testing/#hdr-Main 을 참조하십시오
de-jcup

4
@InancGumuslstat $GOROOT/subtests: no such file or directory
030

1
'code : = m.Run ()'은 다른 TestFunction을 실행하는 것입니다!
Alex Punnen

49

이것은 파일에 init()함수를 넣어서 얻을 수 있습니다 _test.go. 이것은 init()함수 전에 실행 됩니다.

// package_test.go
package main

func init() {
     /* load test data */
}

_test.init ()는 패키지 init () 함수보다 먼저 호출됩니다.


2
나는 당신이 당신의 질문에 답하고 있다는 것을 알고 있으므로 이것은 아마도 당신의 사용 사례를 만족시킬 것입니다. 그러나 이것은 당신이 질문에 포함시킨 NUnit 예제와 동일하지 않습니다.
James Henstridge

@james, 저는 문제에 대한 답을 찾는 방법에 대한 한 가지 생각을 보여줬고 다른 사람들은 이미 당신을 포함하여 좋은 통찰력을 제공했습니다. 접근 방식을 조정하기 위해 외부 영향을 얻는 것이 유용합니다. 감사.
miltonb

2
그럴 수 있지. 이 답변에서 보여준 것은 [TestFixtureSetUp]대신 NUnit의 속성 을 사용하는 것과 다소 비슷합니다 .
James Henstridge

2
그것은 분해 부분을 포함하지 않습니다
Taras Matsyk

7
테스트 파일이 주 기능과 동일한 패키지에있는 경우 이는 좋은 솔루션이 아닙니다.
MouseWanted

28

단위 테스트에 대한 간단한 함수가 주어지면 :

package math

func Sum(a, b int) int {
    return a + b
}

분해 기능을 반환하는 설정 기능으로 테스트 할 수 있습니다. 그리고 setup ()을 호출 한 후 teardown ()을 지연 호출 할 수 있습니다.

package math

import "testing"

func setupTestCase(t *testing.T) func(t *testing.T) {
    t.Log("setup test case")
    return func(t *testing.T) {
        t.Log("teardown test case")
    }
}

func setupSubTest(t *testing.T) func(t *testing.T) {
    t.Log("setup sub test")
    return func(t *testing.T) {
        t.Log("teardown sub test")
    }
}

func TestAddition(t *testing.T) {
    cases := []struct {
        name     string
        a        int
        b        int
        expected int
    }{
        {"add", 2, 2, 4},
        {"minus", 0, -2, -2},
        {"zero", 0, 0, 0},
    }

    teardownTestCase := setupTestCase(t)
    defer teardownTestCase(t)

    for _, tc := range cases {
        t.Run(tc.name, func(t *testing.T) {
            teardownSubTest := setupSubTest(t)
            defer teardownSubTest(t)

            result := Sum(tc.a, tc.b)
            if result != tc.expected {
                t.Fatalf("expected sum %v, but got %v", tc.expected, result)
            }
        })
    }
}

Go 테스트 도구는 셸 콘솔에 로깅 문을보고합니다.

% go test -v
=== RUN   TestAddition
=== RUN   TestAddition/add
=== RUN   TestAddition/minus
=== RUN   TestAddition/zero
--- PASS: TestAddition (0.00s)
    math_test.go:6: setup test case
    --- PASS: TestAddition/add (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/minus (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    --- PASS: TestAddition/zero (0.00s)
        math_test.go:13: setup sub test
        math_test.go:15: teardown sub test
    math_test.go:8: teardown test case
PASS
ok      github.com/kare/go-unit-test-setup-teardown 0.010s
% 

이 접근 방식을 사용하여 설정 / 해체에 몇 가지 추가 매개 변수를 전달할 수 있습니다.


2
이제 그것은 정말 간단하지만 효과적인 트릭입니다. Go 구문을 잘 사용합니다.
miltonb

1
네,하지만 중첩을 증가시킵니다 ( 자바 스크립트에서 일종의 운명의 피라미드 ). 또한 테스트는 외부 테스트에서와 같이 제품군에 의해 자동으로 실행되지 않습니다.
Inanc

12

일반적으로 이동중인 테스트는 다른 언어와 동일한 스타일로 작성되지 않습니다. 종종 비교적 적은 수의 테스트 함수가 있지만 각각에는 테이블 기반 테스트 사례 집합이 포함됩니다. Go 팀 중 한 사람이 작성한이 기사를 참조하십시오 .

테이블 기반 테스트에서는 테이블에 지정된 개별 테스트 케이스를 실행하는 루프 앞에 설정 코드를 넣고 나중에 정리 코드를 넣으면됩니다.

테스트 함수간에 설정 코드를 공유하고있는 경우 공유 설정 코드를 함수로 추출하고 sync.Once정확히 한 번 실행하는 것이 중요한 경우 a를 사용할 수 있습니다 (또는 다른 답변에서 알 수 있듯이을 사용 init()하지만 설정이 단점이 있습니다. 테스트 케이스가 실행되지 않은 경우에도 수행됩니다 (를 사용하여 테스트 케이스를 제한했기 때문일 수 있습니다 go test -run <regexp>.)

정확히 한 번 실행되는 서로 다른 테스트간에 공유 설정이 필요하다고 생각한다면 실제로 필요한지, 테이블 기반 테스트가 더 좋지 않을지 생각 해봐야합니다.


6
이는 플래그 파서 나 숫자에 영향을 미치는 알고리즘과 같은 사소한 것들을 테스트 할 때 좋습니다. 그러나 모두 유사한 상용구 코드가 필요한 다양한 기능을 테스트하려고 할 때는 실제로 도움이되지 않습니다. 내 테스트 함수를 배열로 정의하고 반복 할 수 있다고 생각하지만, 테스트 프레임 워크 자체에 실제로 빌드되어야하는 간단한 루프만큼 테이블 기반이 아닙니다 (적절한 테스트 스위트의 형태로 설정 / 해체 기능 포함)
iamtheddrman

9

Go 테스트 프레임 워크에는 NUnit의 SetUp 속성 (스위트의 각 테스트 전에 호출 할 함수 표시)에 해당하는 것이 없습니다 . 하지만 몇 가지 옵션이 있습니다.

  1. SetUp필요한 곳에서 각 테스트에서 함수를 호출하기 만하면 됩니다.

  2. xUnit 패러다임과 개념을 구현하는 Go의 테스트 프레임 워크 확장을 사용합니다. 세 가지 강력한 옵션이 떠 오릅니다.

이러한 각 라이브러리는 테스트를 다른 xUnit 프레임 워크와 유사한 스위트 / 픽스처로 구성하도록 권장하고 각 메소드 이전에 스위트 / 픽스처 유형에 대한 설정 메소드를 호출합니다 Test*.


0

이 문제를 정확히 해결하기 위해 뻔뻔한 플러그, https://github.com/houqp/gtest 를 만들었습니다 .

다음은 간단한 예입니다.

import (
  "strings"
  "testing"
  "github.com/houqp/gtest"
)

type SampleTests struct{}

// Setup and Teardown are invoked per test group run
func (s *SampleTests) Setup(t *testing.T)      {}
func (s *SampleTests) Teardown(t *testing.T)   {}
// BeforeEach and AfterEach are invoked per test run
func (s *SampleTests) BeforeEach(t *testing.T) {}
func (s *SampleTests) AfterEach(t *testing.T)  {}

func (s *SampleTests) SubTestCompare(t *testing.T) {
  if 1 != 1 {
    t.FailNow()
  }
}

func (s *SampleTests) SubTestCheckPrefix(t *testing.T) {
  if !strings.HasPrefix("abc", "ab") {
    t.FailNow()
  }
}

func TestSampleTests(t *testing.T) {
  gtest.RunSubTests(t, &SampleTests{})
}

각기 다른 설정 / 해체 루틴을 사용하여 패키지 내에서 원하는 테스트 그룹을 만들 수 있습니다.

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