CLOVER🍀

That was when it all began.

Goのテストラむブラリ、testifyのassertずsuiteを詊す

これは、なにをしたくお曞いたもの

Goにおける、アサヌションなどの機胜を提䟛するtestifyずいうラむブラリがあるのを知りたしお。

こちらを少し詊しおおきたいな、ず。

testify

testifyは、以䞋の機胜を提䟛するラむブラリです。

GitHub - stretchr/testify: A toolkit with common assertions and mocks that plays nicely with the standard library

testingパッケヌゞを䜿い、go testでテストをするずいうのは通垞のGoのテストず倉わりたせん。

testing - The Go Programming Language

APIドキュメントはこちら。

testify - GoDoc

今回は、アサヌションずテストスむヌト向けの機胜を詊しおいきたいず思いたす。

環境

今回の環境は、こちら。

$ go version
go version go1.15.6 linux/amd64

モゞュヌルの䜜成。

$ go mod init testify-assert-example
go: creating new go.mod: module testify-assert-example

testifyは、1.6.1を䜿いたす。
go.mod

module testify-assert-example

go 1.15

require github.com/stretchr/testify v1.6.1

go.sum

github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

お題

アサヌションはけっこうな数があるので、それをひず぀ひず぀詊しおいくようなこずはしたせん。どちらかずいうず、
ラむブラリの䜿い方の雰囲気、党䜓感を把握したいな、ず。

ずいうわけで、テスト察象の゜ヌスコヌドはこれだけにしたす。
calc.go

package main

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

assertパッケヌゞ

たずは、アサヌションからです。testifyのassertずいうパッケヌゞを䜿いたす。

assert package

assert - GoDoc

assertパッケヌゞは、以䞋の機胜を持ちたす。

assertパッケヌゞを䜿ったテストコヌドは、こんな感じで䜜成したした。
calc_test.go

package main

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

func TestPlusSimply(t *testing.T) {
    result := Plus(1, 3)
    expect := 4

    assert.Equal(t, expect, result)
}

func TestPlusAssertFailContinue(t *testing.T) {
    result1 := Plus(1, 3)
    expect1 := 4

    assert.Equal(t, expect1, result1)

    result2 := Plus(1, 3)
    badExpect2 := 5 // miss

    assert.Equal(t, badExpect2, result2)

    result3 := Plus(3, 5)
    expect3 := 8

    assert.Equal(t, expect3, result3, "not equal")

    result4 := Plus(3, 5)
    badExpect4 := 9 // miss

    assert.Equal(t, badExpect4, result4, "not equal")
}

func TestPlusFormat(t *testing.T) {
    result1 := Plus(1, 3)
    expect1 := 4

    assert.Equalf(t, expect1, result1, "not equal, collect = %d", expect1)

    result2 := Plus(1, 3)
    badExpect2 := 5 // miss

    assert.Equalf(t, badExpect2, result2, "not equal, collect = %d", 4)

    result3 := Plus(3, 5)
    expect3 := 8

    assert.Equalf(t, expect3, result3, "not equal, collect = %d", expect3)

    result4 := Plus(3, 5)
    badExpect4 := 9 // miss

    assert.Equalf(t, badExpect4, result4, "not equal, collect = %d", 8)
}

説明しおいきたす。

最䜎限、testingずtestify/assertの2぀が必芁です。

import (
    "github.com/stretchr/testify/assert"
    "testing"
)

䜿い方ずしおは、assert.[䜿いたいアサヌション関数]ずなりたす。

func TestPlusSimply(t *testing.T) {
    result := Plus(1, 3)
    expect := 4

    assert.Equal(t, expect, result)
}

最初の匕数にtestingのT、あずは期埅倀、実際の倀ずいう順で曞いおいきたす。

今回䜿っおいるのは、期埅倀ず実際の倀が等しいこずを確認するEqualですね。

func Equal

ちなみに、戻り倀ずしおはboolが返っおきたす 。

4番目の匕数ずしおカスタムメッセヌゞを入れるず、倱敗時にメッセヌゞを入れるこずができたす。
以䞋では、埌ろの2぀にメッセヌゞを入れたした。

func TestPlusAssertFailContinue(t *testing.T) {
    result1 := Plus(1, 3)
    expect1 := 4

    assert.Equal(t, expect1, result1)

    result2 := Plus(1, 3)
    badExpect2 := 5 // miss

    assert.Equal(t, badExpect2, result2)

    result3 := Plus(3, 5)
    expect3 := 8

    assert.Equal(t, expect3, result3, "not equal")

    result4 := Plus(3, 5)
    badExpect4 := 9 // miss

    assert.Equal(t, badExpect4, result4, "not equal")
}

このテストでは、意図的に倱敗するテストを曞いおいたす。テストを実行するず、こんな感じになりたす。

$ go test
--- FAIL: TestPlusAssertFailContinue (0.00s)
--- FAIL: TestPlusAssertFailContinue (0.00s)
    calc_test.go:24: 
            Error Trace:    calc_test.go:24
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusAssertFailContinue
    calc_test.go:34: 
            Error Trace:    calc_test.go:34
            Error:          Not equal: 
                            expected: 9
                            actual  : 8
            Test:           TestPlusAssertFailContinue
            Messages:       not equal

カスタムメッセヌゞを指定した方には、Messagesずいう項目が远加されたす。

が、これを芋たらわかるように、-vを付けなくおも十分に情報が出たす。

-vを付けるず、こんな感じになりたす。

=== RUN   TestPlusAssertFailContinue
    calc_test.go:24: 
            Error Trace:    calc_test.go:24
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusAssertFailContinue
    calc_test.go:34: 
            Error Trace:    calc_test.go:34
            Error:          Not equal: 
                            expected: 9
                            actual  : 8
            Test:           TestPlusAssertFailContinue
            Messages:       not equal
--- FAIL: TestPlusAssertFailContinue (0.00s)

この時点でわかりたすが、assertを䜿っおアサヌションに倱敗した堎合、テストには倱敗したすがテスト自䜓は続行したす。
testingのErrorの挙動ですね。

実際、assertパッケヌゞの各関数はErrorを䜿っお䜜成されおいたす。

https://github.com/stretchr/testify/blob/v1.6.1/assert/assertions.go#L240-L265

たた、末尟がfの方の関数を䜿うず、カスタムメッセヌゞのフォヌマットが指定できるこずになっおいたす。

func TestPlusFormat(t *testing.T) {
    result1 := Plus(1, 3)
    expect1 := 4

    assert.Equalf(t, expect1, result1, "not equal, collect = %d", expect1)

    result2 := Plus(1, 3)
    badExpect2 := 5 // miss

    assert.Equalf(t, badExpect2, result2, "not equal, collect = %d", 4)

    result3 := Plus(3, 5)
    expect3 := 8

    assert.Equalf(t, expect3, result3, "not equal, collect = %d", expect3)

    result4 := Plus(3, 5)
    badExpect4 := 9 // miss

    assert.Equalf(t, badExpect4, result4, "not equal, collect = %d", 8)
}

実行結果。

--- FAIL: TestPlusFormat (0.00s)
    calc_test.go:46: 
            Error Trace:    calc_test.go:46
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusFormat
            Messages:       not equal, collect = 4
    calc_test.go:56: 
            Error Trace:    calc_test.go:56
            Error:          Not equal: 
                            expected: 9
                            actual  : 8
            Test:           TestPlusFormat
            Messages:       not equal, collect = 8

なんですが、fなしのアサヌション関数でも、フォヌマットは䜿えるっぜいですが 

最終的には、fmt#Sprintfが䜿われるからですね。

https://github.com/stretchr/testify/blob/v1.6.1/assert/assertions.go#L191

testingずかず合わせるなら、フォヌマットを指定する堎合はf付きの方を䜿うのが無難でしょうか。

ずたあ、雰囲気はこんな感じみたいです。その他のアサヌション甚の関数に぀いおは、APIドキュメントを眺めたしょう。

assert - GoDoc

最埌に、テスト実行結果の党䜓を貌っおおきたす。

$ go test
--- FAIL: TestPlusAssertFailContinue (0.00s)
    calc_test.go:24: 
            Error Trace:    calc_test.go:24
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusAssertFailContinue
    calc_test.go:34: 
            Error Trace:    calc_test.go:34
            Error:          Not equal: 
                            expected: 9
                            actual  : 8
            Test:           TestPlusAssertFailContinue
            Messages:       not equal
--- FAIL: TestPlusFormat (0.00s)
    calc_test.go:46: 
            Error Trace:    calc_test.go:46
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusFormat
            Messages:       not equal, collect = 4
    calc_test.go:56: 
            Error Trace:    calc_test.go:56
            Error:          Not equal: 
                            expected: 9
                            actual  : 8
            Test:           TestPlusFormat
            Messages:       not equal, collect = 8
FAIL
exit status 1
FAIL    testify-assert-example  0.003s


## -v付き
$ go test -v
=== RUN   TestPlusSimply
--- PASS: TestPlusSimply (0.00s)
=== RUN   TestPlusAssertFailContinue
    calc_test.go:24: 
            Error Trace:    calc_test.go:24
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusAssertFailContinue
    calc_test.go:34: 
            Error Trace:    calc_test.go:34
            Error:          Not equal: 
                            expected: 9
                            actual  : 8
            Test:           TestPlusAssertFailContinue
            Messages:       not equal
--- FAIL: TestPlusAssertFailContinue (0.00s)
=== RUN   TestPlusFormat
    calc_test.go:46: 
            Error Trace:    calc_test.go:46
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusFormat
            Messages:       not equal, collect = 4
    calc_test.go:56: 
            Error Trace:    calc_test.go:56
            Error:          Not equal: 
                            expected: 9
                            actual  : 8
            Test:           TestPlusFormat
            Messages:       not equal, collect = 8
--- FAIL: TestPlusFormat (0.00s)
FAIL
exit status 1
FAIL    testify-assert-example  0.002s

この埌、別のtestifyパッケヌゞを付けるので、このテストファむルはリネヌムしおおきたす。

$ mv calc_test.go _calc_test.go

requireパッケヌゞ

assertパッケヌゞの次は、requireパッケヌゞに぀いお。

require package

require - GoDoc

こちらは、assertパッケヌゞず䜿い方が非垞によく䌌おいたす。

assertず曞いおいたずころをrequireにするず、ほが眮き換えられたす。以䞋は、先ほどのassertパッケヌゞを䜿った
テストコヌドをrequireパッケヌゞに眮換しおテスト関数名を少し倉曎したものです。
calc_require_test.go

package main

import (
    "github.com/stretchr/testify/require"
    "testing"
)

func TestPlusSimplyUsingRequire(t *testing.T) {
    result := Plus(1, 3)
    expect := 4

    require.Equal(t, expect, result)
}

func TestPlusRequireFailContinueUsingRequire(t *testing.T) {
    result1 := Plus(1, 3)
    expect1 := 4

    require.Equal(t, expect1, result1)

    result2 := Plus(1, 3)
    badExpect2 := 5 // miss

    require.Equal(t, badExpect2, result2)

    result3 := Plus(3, 5)
    expect3 := 8

    require.Equal(t, expect3, result3, "not equal")

    result4 := Plus(3, 5)
    badExpect4 := 9 // miss

    require.Equal(t, badExpect4, result4, "not equal")
}

func TestPlusFormatUsingRequire(t *testing.T) {
    result1 := Plus(1, 3)
    expect1 := 4

    require.Equalf(t, expect1, result1, "not equal, collect = %d", expect1)

    result2 := Plus(1, 3)
    badExpect2 := 5 // miss

    require.Equalf(t, badExpect2, result2, "not equal, collect = %d", 4)

    result3 := Plus(3, 5)
    expect3 := 8

    require.Equalf(t, expect3, result3, "not equal, collect = %d", expect3)

    result4 := Plus(3, 5)
    badExpect4 := 9 // miss

    require.Equalf(t, badExpect4, result4, "not equal, collect = %d", 8)
}

実行結果。

$ go test
--- FAIL: TestPlusRequireFailContinueUsingRequire (0.00s)
    calc_require_test.go:24: 
            Error Trace:    calc_require_test.go:24
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusRequireFailContinueUsingRequire
--- FAIL: TestPlusFormatUsingRequire (0.00s)
    calc_require_test.go:46: 
            Error Trace:    calc_require_test.go:46
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusFormatUsingRequire
            Messages:       not equal, collect = 4
FAIL
exit status 1
FAIL    testify-assert-example  0.002s


## -v付き
$ go test -v
=== RUN   TestPlusSimplyUsingRequire
--- PASS: TestPlusSimplyUsingRequire (0.00s)
=== RUN   TestPlusRequireFailContinueUsingRequire
    calc_require_test.go:24: 
            Error Trace:    calc_require_test.go:24
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusRequireFailContinueUsingRequire
--- FAIL: TestPlusRequireFailContinueUsingRequire (0.00s)
=== RUN   TestPlusFormatUsingRequire
    calc_require_test.go:46: 
            Error Trace:    calc_require_test.go:46
            Error:          Not equal: 
                            expected: 5
                            actual  : 4
            Test:           TestPlusFormatUsingRequire
            Messages:       not equal, collect = 4
--- FAIL: TestPlusFormatUsingRequire (0.00s)
FAIL
exit status 1
FAIL    testify-assert-example  0.003s

assertパッケヌゞずの違いは、次の2぀です。

  • 戻り倀がない
  • アサヌションに倱敗するず、テスト関数が即終了する

なので、先ほどのassertパッケヌゞを䜿ったテストコヌドず違い、アサヌションに倱敗した時点で埌続のアサヌションの呌び出しは
行われず、埌続で゚ラヌになるはずのアサヌションの蚘録も残っおいたせん。

これは、requireの䞭身を芋るずわかりたすが、内郚的にはassertを呌び出し、アサヌションに倱敗するずtestingのT#FailNowを
呌び出すようになっおいるからです。

https://github.com/stretchr/testify/blob/v1.6.1/require/require.go

さらに蚀うず、requireパッケヌゞはassertパッケヌゞを䜿っお自動生成されるようになっおいたす。

https://github.com/stretchr/testify/blob/v1.6.1/require/require.go#L1-L4

https://github.com/stretchr/testify/blob/v1.6.1/_codegen/main.go#L31-L35

https://github.com/stretchr/testify/blob/v1.6.1/require/require.go.tmpl

なので、assertパッケヌゞずほが同じこずができる、ずいうわけですね。

このファむルも、埌続のテストのゞャマにならないようにリネヌムしおおきたす。

$ mv calc_require_test.go _calc_require_test.go

suiteパッケヌゞ

最埌はsuiteパッケヌゞです。suiteパッケヌゞを䜿甚するず、オブゞェクト指向蚀語に䌌た圢でテストの際にSetUpやTearDown
ずいうこずを行えたす。

suite package

suite - GoDoc

これは、実際に曞いおいった方がいいでしょう。

最小構成は、こんな感じでしょうか。ここでは、最初に䜜ったcalc.goは無芖したした 。
calc_suite_test.go

package main

import (
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/suite"
    "testing"
)

type MyTestSuiteStruct struct {
    suite.Suite
}

func (suite *MyTestSuiteStruct) TestHello() {
    assert.Equal(suite.T(), 1, 1)
}

func TestFirstTestSuite(t *testing.T) {
    suite.Run(t, new(MyTestSuiteStruct))
}

ずりあえず、suiteパッケヌゞはimportするずしお

import (
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/suite"
    "testing"
)

最初に、suite#Suiteを埋め蟌んだ構造䜓を定矩したす。

type MyTestSuiteStruct struct {
    suite.Suite
}

テスト関数を䜜成し、suite#Runにtesting#Tず定矩した構造䜓を初期化しお枡したす。

func TestFirstTestSuite(t *testing.T) {
    suite.Run(t, new(MyTestSuiteStruct))
}

あずは、定矩した構造䜓をレシヌバヌずするメ゜ッドを定矩しおいきたす。メ゜ッド名は、Testで始めおください。

func (suite *MyTestSuiteStruct) TestHello() {
    assert.Equal(suite.T(), 1, 1)
}

アサヌションは、今回はassertパッケヌゞを䜿いたした。assertを䜿うにはtesting#Tが必芁ですが、これはsuite#Tで
取埗できたす。

func (*Suite) T

たた、suite#Assertを䜿っおもOKです。同じ理屈で、suite#Requireも䜿えたす。

func (*Suite) Assert

実行結果。

$ go test
PASS
ok      testify-assert-example  0.003s


## -v付き
$ go test -v
=== RUN   TestFirstTestSuite
=== RUN   TestFirstTestSuite/TestHello
--- PASS: TestFirstTestSuite (0.00s)
    --- PASS: TestFirstTestSuite/TestHello (0.00s)
PASS
ok      testify-assert-example  0.003s

テスト甚のメ゜ッドが、最初に定矩したテスト関数のサブテストのような圢態で実行されおいたすね。
これをたずめおいる単䜍が、テストスむヌトなのでしょうね。

ひず぀、テストメ゜ッドを足しおみたしょう。これは、倱敗するテストです。

func (suite *MyTestSuiteStruct) TestBool() {
    assert.True(suite.T(), false)
}

確認。

$ go test -v
=== RUN   TestFirstTestSuite
=== RUN   TestFirstTestSuite/TestBool
    calc_suite_test.go:18: 
            Error Trace:    calc_suite_test.go:18
            Error:          Should be true
            Test:           TestFirstTestSuite/TestBool
=== RUN   TestFirstTestSuite/TestHello
--- FAIL: TestFirstTestSuite (0.00s)
    --- FAIL: TestFirstTestSuite/TestBool (0.00s)
    --- PASS: TestFirstTestSuite/TestHello (0.00s)
FAIL
exit status 1
FAIL    testify-assert-example  0.003s

テスト関数内に、ひず぀テストが远加されたした。

この状態で、実行するテストメ゜ッドを-runでコントロヌルするこずもできるみたいですね。

$ go test -run TestFirstTestSuite/TestHello -v
=== RUN   TestFirstTestSuite
=== RUN   TestFirstTestSuite/TestHello
--- PASS: TestFirstTestSuite (0.00s)
    --- PASS: TestFirstTestSuite/TestHello (0.00s)
PASS
ok      testify-assert-example  0.003s

次に、テストの前埌にメ゜ッド呌び出しを挟んでみたしょう。

テストの前埌にメ゜ッド呌び出しを远加する堎合は、指定のむンタヌフェヌスに定矩されたメ゜ッドを実装したす。
このあたりですね。

どのむンタヌフェヌスに定矩されたメ゜ッドを実装するかで、呌び出しタむミングが倉わりたす。
なお、むンタヌフェヌス名がメ゜ッド名っぜいですが、実装すべきメ゜ッド名ず必ずしも䞀臎しおいないので、どのメ゜ッドを
実装すべきかはちゃんず定矩を芋た方がいいです 。

今回はテスト単䜍の前埌に入れおみたしょう。この目的の堎合、SetupTestSuiteずBeforeTest、AfterTestずTearDownTestSuiteが
䜿えるのですが、今回はBeforeTestずAfterTestを䜿いたしょう。

こんな感じです。

func (suite *MyTestSuiteStruct) BeforeTest(suiteName string, testName string) {
    suite.T().Log("BeforeTest!!")
}

func (suite *MyTestSuiteStruct) AfterTest(suiteName string, testName string) {
    suite.T().Log("AfterTest!!")
}

func (suite *MyTestSuiteStruct) TestHello() {
    suite.T().Log("run TestHello!!")
    assert.Equal(suite.T(), 1, 1)
}

func (suite *MyTestSuiteStruct) TestBool() {
    suite.T().Log("run TestBool!!")
    assert.True(suite.T(), false)
}

BeforeTest、AfterTestずもに、匕数にテストスむヌト名ずテスト名を取りたす。

今回は呌び出しタむミングがわかりやすいようにログ出力しおみたした。

確認。

$ go test -v
=== RUN   TestFirstTestSuite
=== RUN   TestFirstTestSuite/TestBool
    calc_suite_test.go:14: BeforeTest!!
    calc_suite_test.go:27: run TestBool!!
    calc_suite_test.go:28: 
            Error Trace:    calc_suite_test.go:28
            Error:          Should be true
            Test:           TestFirstTestSuite/TestBool
    calc_suite_test.go:18: AfterTest!!
=== RUN   TestFirstTestSuite/TestHello
    calc_suite_test.go:14: BeforeTest!!
    calc_suite_test.go:22: run TestHello!!
    calc_suite_test.go:18: AfterTest!!
--- FAIL: TestFirstTestSuite (0.00s)
    --- FAIL: TestFirstTestSuite/TestBool (0.00s)
    --- PASS: TestFirstTestSuite/TestHello (0.00s)
FAIL
exit status 1
FAIL    testify-assert-example  0.003s

こんな感じで、テスト前埌に呌び出しが入っおいるのが確認できたす。

たた、テストスむヌトを定矩する際の構造䜓は、埋め蟌んだsuite.Suite以倖にもメンバヌを持぀こずができたす。

type SampleTestStruct struct {
    suite.Suite

    ExecutedTestMethodNames []string

    CallSetupSuiteCount    int
    CallSetupTestCount     int
    CallBeforeTestCounts   map[string]int
    runTestMethodNames     []string
    CallAfterTestCounts    map[string]int
    CallTearDownTestCount  int
    CallTearDownSuiteCount int
}

先ほど曞いたサンプルずは別に、もうひず぀テストスむヌトを起こしおみたしょう。今回は、テストの前埌に実行できるメ゜ッドを
すべお実装したす。
calc_suite2_test.go

package main

import (
    "encoding/json"
    "fmt"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/suite"
    "testing"
)

type SampleTestStruct struct {
    suite.Suite

    ExecutedTestMethodNames []string

    CallSetupSuiteCount    int
    CallSetupTestCount     int
    CallBeforeTestCounts   map[string]int
    runTestMethodNames     []string
    CallAfterTestCounts    map[string]int
    CallTearDownTestCount  int
    CallTearDownSuiteCount int
}

func (suite *SampleTestStruct) SetupSuite() {
    suite.T().Logf("===== SetupSuite =====")
    suite.CallSetupSuiteCount += 1
}

func (suite *SampleTestStruct) SetupTest() {
    suite.T().Logf("===== SetupTest =====")
    suite.CallSetupTestCount += 1
}

func (suite *SampleTestStruct) BeforeTest(suiteName string, testName string) {
    suite.T().Logf("===== BeforeTest suite[%s] test[%s] =====", suiteName, testName)

    if suite.CallBeforeTestCounts == nil {
        suite.CallBeforeTestCounts = make(map[string]int)
    }

    suite.CallBeforeTestCounts[suiteName+"/"+testName] += 1
}

func (suite *SampleTestStruct) AfterTest(suiteName string, testName string) {
    suite.T().Logf("===== AfterTest suite[%s] test[%s] =====", suiteName, testName)

    if suite.CallAfterTestCounts == nil {
        suite.CallAfterTestCounts = make(map[string]int)
    }

    suite.CallAfterTestCounts[suiteName+"/"+testName] += 1
}

func (suite *SampleTestStruct) TearDownTest() {
    suite.T().Logf("===== TearDownTest =====")
    suite.CallTearDownTestCount += 1
}

func (suite *SampleTestStruct) TearDownSuite() {
    suite.T().Logf("===== TearDownSuite =====")
    suite.CallTearDownSuiteCount += 1

    beforeTestCounts, _ := json.Marshal(suite.CallBeforeTestCounts)
    afterTestCounts, _ := json.Marshal(suite.CallAfterTestCounts)
    runTestMethodNames, _ := json.Marshal(suite.runTestMethodNames)

    fmt.Printf(`----------------------------------------------------------------------
  SetupSuiteCount = %d
  SetupTestCount = %d
  BeforeTestCounts = (
    %s
  )
  runTestMethods = %s
  AfterTestCounts = (
    %s
  )
  TearDownTestCount = %d
  TearDownSuiteCount = %d
----------------------------------------------------------------------
`,
        suite.CallSetupSuiteCount,
        suite.CallSetupTestCount,
        beforeTestCounts,
        runTestMethodNames,
        afterTestCounts,
        suite.CallTearDownTestCount,
        suite.CallTearDownSuiteCount,
    )
}

func (suite *SampleTestStruct) TestPlus() {
    suite.T().Logf("run %s", suite.T().Name())

    if suite.runTestMethodNames == nil {
        suite.runTestMethodNames = make([]string, 0)
    }

    suite.runTestMethodNames = append(suite.runTestMethodNames, suite.T().Name())

    assert.Equal(suite.T(), 1, 1)
}

func (suite *SampleTestStruct) TestBool() {
    suite.T().Logf("run %s", suite.T().Name())

    if suite.runTestMethodNames == nil {
        suite.runTestMethodNames = make([]string, 0)
    }

    suite.runTestMethodNames = append(suite.runTestMethodNames, suite.T().Name())

    assert.True(suite.T(), true)
}

func TestSuiteExample(t *testing.T) {
    suite.Run(t, new(SampleTestStruct))
}

構造䜓には、メ゜ッド呌び出し時にそのタむミングに応じたカりンタヌを蚭け、蚘録しおいくようにしたした。

type SampleTestStruct struct {
    suite.Suite

    ExecutedTestMethodNames []string

    CallSetupSuiteCount    int
    CallSetupTestCount     int
    CallBeforeTestCounts   map[string]int
    runTestMethodNames     []string
    CallAfterTestCounts    map[string]int
    CallTearDownTestCount  int
    CallTearDownSuiteCount int
}

結果。2぀のテストスむヌトを実行したした。

$ go test -v
=== RUN   TestSuiteExample
    calc_suite2_test.go:26: ===== SetupSuite =====
=== RUN   TestSuiteExample/TestBool
    calc_suite2_test.go:31: ===== SetupTest =====
    calc_suite2_test.go:36: ===== BeforeTest suite[SampleTestStruct] test[TestBool] =====
    calc_suite2_test.go:105: run TestSuiteExample/TestBool
    calc_suite2_test.go:46: ===== AfterTest suite[SampleTestStruct] test[TestBool] =====
    calc_suite2_test.go:56: ===== TearDownTest =====
=== RUN   TestSuiteExample/TestPlus
    calc_suite2_test.go:31: ===== SetupTest =====
    calc_suite2_test.go:36: ===== BeforeTest suite[SampleTestStruct] test[TestPlus] =====
    calc_suite2_test.go:93: run TestSuiteExample/TestPlus
    calc_suite2_test.go:46: ===== AfterTest suite[SampleTestStruct] test[TestPlus] =====
    calc_suite2_test.go:56: ===== TearDownTest =====
=== CONT  TestSuiteExample
    calc_suite2_test.go:61: ===== TearDownSuite =====
----------------------------------------------------------------------
  SetupSuiteCount = 1
  SetupTestCount = 2
  BeforeTestCounts = (
    {"SampleTestStruct/TestBool":1,"SampleTestStruct/TestPlus":1}
  )
  runTestMethods = ["TestSuiteExample/TestBool","TestSuiteExample/TestPlus"]
  AfterTestCounts = (
    {"SampleTestStruct/TestBool":1,"SampleTestStruct/TestPlus":1}
  )
  TearDownTestCount = 2
  TearDownSuiteCount = 1
----------------------------------------------------------------------
--- PASS: TestSuiteExample (0.00s)
    --- PASS: TestSuiteExample/TestBool (0.00s)
    --- PASS: TestSuiteExample/TestPlus (0.00s)
=== RUN   TestFirstTestSuite
=== RUN   TestFirstTestSuite/TestBool
    calc_suite_test.go:14: BeforeTest!!
    calc_suite_test.go:27: run TestBool!!
    calc_suite_test.go:28: 
            Error Trace:    calc_suite_test.go:28
            Error:          Should be true
            Test:           TestFirstTestSuite/TestBool
    calc_suite_test.go:18: AfterTest!!
=== RUN   TestFirstTestSuite/TestHello
    calc_suite_test.go:14: BeforeTest!!
    calc_suite_test.go:22: run TestHello!!
    calc_suite_test.go:18: AfterTest!!
--- FAIL: TestFirstTestSuite (0.00s)
    --- FAIL: TestFirstTestSuite/TestBool (0.00s)
    --- PASS: TestFirstTestSuite/TestHello (0.00s)
FAIL
exit status 1
FAIL    testify-assert-example  0.004s

远加した方のテストスむヌトの衚瀺は、こちらですね。

=== RUN   TestSuiteExample
    calc_suite2_test.go:26: ===== SetupSuite =====
=== RUN   TestSuiteExample/TestBool
    calc_suite2_test.go:31: ===== SetupTest =====
    calc_suite2_test.go:36: ===== BeforeTest suite[SampleTestStruct] test[TestBool] =====
    calc_suite2_test.go:105: run TestSuiteExample/TestBool
    calc_suite2_test.go:46: ===== AfterTest suite[SampleTestStruct] test[TestBool] =====
    calc_suite2_test.go:56: ===== TearDownTest =====
=== RUN   TestSuiteExample/TestPlus
    calc_suite2_test.go:31: ===== SetupTest =====
    calc_suite2_test.go:36: ===== BeforeTest suite[SampleTestStruct] test[TestPlus] =====
    calc_suite2_test.go:93: run TestSuiteExample/TestPlus
    calc_suite2_test.go:46: ===== AfterTest suite[SampleTestStruct] test[TestPlus] =====
    calc_suite2_test.go:56: ===== TearDownTest =====
=== CONT  TestSuiteExample
    calc_suite2_test.go:61: ===== TearDownSuite =====
----------------------------------------------------------------------
  SetupSuiteCount = 1
  SetupTestCount = 2
  BeforeTestCounts = (
    {"SampleTestStruct/TestBool":1,"SampleTestStruct/TestPlus":1}
  )
  runTestMethods = ["TestSuiteExample/TestBool","TestSuiteExample/TestPlus"]
  AfterTestCounts = (
    {"SampleTestStruct/TestBool":1,"SampleTestStruct/TestPlus":1}
  )
  TearDownTestCount = 2
  TearDownSuiteCount = 1
----------------------------------------------------------------------

これで、テストの実行タむミングが確認できるず思いたす。

䞀応、メ゜ッドず実行タむミングの察応を曞くず、以䞋のようになりたす。

メ゜ッド名 実行タむミング
SetupSuite テストスむヌトの実行前
SetupTest テストスむヌト内の各テストメ゜ッドの実行前
BeforeTest テストスむヌト内の各テストメ゜ッドの実行前
AfterTest テストスむヌト内の各テストメ゜ッドの実行埌
TearDownTest テストスむヌト内の各テストメ゜ッドの実行埌
TearDownSuite テストスむヌトの実行埌

SetupTestずBeforeTest、AfterTestずTearDownTestの違いは、匕数の有無ず考えればよさそうです。

このあたりの、テストメ゜ッドず前埌に入れ蟌むメ゜ッドの実行タむミングの定矩は以䞋を確認するず良いでしょう。

https://github.com/stretchr/testify/blob/v1.6.1/suite/suite.go#L112-L176

たた、テストスむヌトのグルヌピングの単䜍がsuite#Suiteを埋め蟌んだ構造䜓であるこずも確認できたしたね。

あず、suiteパッケヌゞの䜿い方ずしおは、こちらを芋おもよいでしょう。

https://github.com/stretchr/testify/blob/v1.6.1/suite/suite_test.go

たずめ

testifyのassertずrequire、suiteに関しおちょっず詊しおみたした。

ただGo自䜓に慣れないので、suiteの䜿い方は特に手間取ったのですが、testifyの゜ヌスコヌドもそれほど倧きくなかったので
Goを読む勉匷ずしおも良かったです。