CLOVER🍀

That was when it all began.

go testingを試す

これは、なにをしたくて書いたもの?

Goでのテストの書き方を学んでみようかなと。

Goにはテストの仕組みが標準で組み込まれていて、go testコマンドとtestingパッケージを使うみたいです。

testing - The Go Programming Language

環境

今回の環境は、こちら。

$ go version
go version go1.15.6 linux/amd64

testingパッケージ

testingパッケージは、Goでのテストをサポートするパッケージです。go testコマンドと組み合わせて使用します。

testing - The Go Programming Language

testingパッケージにはベンチマークも含まれているようですが、今回は扱いません。

Benchmarks

また、ドキュメント以外にも、goコマンドのヘルプも見ておいた方が良いでしょう。ドキュメントにはない情報が
載っていたりします。

$ go help test
$ go help testfunc
テストの条件

testingパッケージを使ってテストを書くには、以下のステップを踏みます。

  • 名前が_test.goで終わるファイルを作成する
    • テストするパッケージと同じパッケージに配置すること
    • このファイルは、通常のビルドからは除外される
    • ファイル名が_または.で始まる場合は、無視される
  • 関数名がTestで始まる、テスト実行用の関数を作成する
    • Testに続く文字は、小文字はNG
      • ○ TestSample、× Testsample
  • go testでテストを実行する
  • testdataディレクトリにテストデータを置くことができる

あとはTを使ってテストを書いていきます。

type T

テスト結果はキャッシュされる模様。
また、Goにはアサーションの仕組みはないようです。

Frequently Asked Questions (FAQ) / Why does Go not have assertions?

まあ、とりあえず始めてみましょう。

準備

最初にモジュールを作っておきます。

$ go mod init testing-getting-started
go: creating new go.mod: module testing-getting-started

パッケージを作って、その中に足し算、引き算を行う関数を作ります。
sample/calc.go

package sample

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

func Minus(a int, b int) int {
    return a - b
}

動作確認のためのソースコードも作りましょう。
main.go

package main

import (
    "fmt"
    "testing-getting-started/sample"
)

func main() {
    fmt.Printf("1 + 3 = %d\n", sample.Plus(1, 3))
    fmt.Printf("5 - 2 = %d\n", sample.Minus(5, 2))
}

確認。

$ go run main.go 
1 + 3 = 4
5 - 2 = 3

これで、下準備はOKでしょう。

testingのAPIを触ってみる

とりあえず、testingのAPIを触ってみます。APIを知るために書いているので、ここではなにかの処理結果を確認したりはしません。
main_test.go

package main

import (
    "fmt"
    "testing"
)

func TestSample(t *testing.T) {
    fmt.Println("Hello World!!")
}

ドキュメントにあった通り、Testで始まる関数名にして、引数をTにします。

func TestSample(t *testing.T) {

確認。go testを実行します。

$ go test
Hello World!!
PASS
ok      testing-getting-started 0.001s

OKになりました。

-vフラグを使うと、もうちょっと詳細に見ることができます。

$ go test -v
=== RUN   TestSample
Hello World!!
--- PASS: TestSample (0.00s)
PASS
ok      testing-getting-started 0.001s

確かに、作成したテストが実行されているようです。

ところで、ファイル名が_や.で始まる場合は無視されるということでした。確認してみましょう。

ファイル名を変更。

$ mv main_test.go _main_test.go

確認。確かに無視されますね。

$ go test
?       testing-getting-started [no test files]


$ go test -v
?       testing-getting-started [no test files]

元に戻しておきます。

$ mv _main_test.go main_test.go

テスト内では、Tに対するメソッドを使うようです。

type T

以下のようなものがあります(全部ではありません)。

  • Error / Errorf
  • Fatal / Fatalf
  • Fail / FailNow
  • Log / Logf
  • Skip / Skipf / SkipNow
  • TempDir

Logを使ってみます。
main_test.go

package main

import (
    //"fmt"
    "testing"
)

func TestSample(t *testing.T) {
    // fmt.Println("Hello World!!")

    t.Log("Hello Testing!!")
}

go testではなにも出力されません。

$ go test
PASS
ok      testing-getting-started 0.001s

-v付きで実行すると、ログが出力されます。

$ go test -v
=== RUN   TestSample
    main_test.go:11: Hello Testing!!
--- PASS: TestSample (0.00s)
PASS
ok      testing-getting-started 0.002s

続けて、Failを使ってみましょう。

Failを使うとテストが失敗します。

func (*T) Fail

Failの呼び出しを追加して

func TestSample(t *testing.T) {
    // fmt.Println("Hello World!!")

    t.Log("Hello Testing!!")

    t.Fail()
}

テストを実行すると、失敗します。

$ go test
--- FAIL: TestSample (0.00s)
    main_test.go:11: Hello Testing!!
FAIL
exit status 1
FAIL    testing-getting-started 0.001s

この時、よく見ると-vを付けていないのにLogの内容が出力されています。

func (*T) Log

ここで、Logの説明を読んでみると、

For tests, the text will be printed only if the test fails or the -test.v flag is set.

とあり、テストが失敗した時または-vフラグを付けてテストを実行した時に出力されるとあります。テストが問題ない時には
出さなくてもよい、ということですね。

ところで、Failに似たものとしてFailNowがあります。

func (*T) FailNow

2つの違いは、呼び出し後に継続するかどうかです。

Failの後に、ログを追加してみましょう。

func TestSample(t *testing.T) {
    // fmt.Println("Hello World!!")

    //t.Log("Hello Testing!!")

    t.Log("Test start")

    t.Fail()

    t.Log("Test end")
}

確認。Failの後に記録したログも出力されています。

$ go test
--- FAIL: TestSample (0.00s)
    main_test.go:13: Test start
    main_test.go:17: Test end
FAIL
exit status 1
FAIL    testing-getting-started 0.001s

では、FailをFailNowに変更してみます。

func TestSample(t *testing.T) {
    // fmt.Println("Hello World!!")

    //t.Log("Hello Testing!!")

    t.Log("Test start")

    //t.Fail()
    t.FailNow()

    t.Log("Test end")
}

すると、FailNow呼び出し後のログは記録されなくなります。

$ go test
--- FAIL: TestSample (0.00s)
    main_test.go:13: Test start
FAIL
exit status 1
FAIL    testing-getting-started 0.002s

Nowが付くもの、付かないもの両方がある場合は、即時終了するのがNowで、そうでないものはそのまま継続するという
ことですね。

Skipは違うみたいですけどね…。

Skip is equivalent to Log followed by SkipNow.

func (*T) Skip

ただ、実際にはFailやFailNowは使わず、ErrorやFatalを使うことになりそうですけどね。

先ほどのテストコードは、すっきりさせておきます。
main_test.go

package main

import (
    "testing"
)

func TestSample(t *testing.T) {
    t.Log("Hello Testing!!")
}

テストを書く

ここまでは、LogとかFailとかしか見てこなかったのですが、ここからは最初に用意した足し算、引き算に対するテストを
書いていきます。

テスト対象の関数はsampleパッケージに置いていたので、テストコードもsampleパッケージに配置します。
こんな感じで書いてみました。
sample/calc_test.go

package sample

import (
    "testing"
)

func TestPlusGood(t *testing.T) {
    t.Logf("[Start %s]", t.Name())

    expect := 3

    if Plus(1, 2) != expect {
        t.Fatalf("Plus(%d, %d) should be %d, but doesn't match", 1, 2, expect)
    }

    t.Logf("[End %s]", t.Name())
}

func TestPlusBad(t *testing.T) {
    t.Logf("[Start %s]", t.Name())

    expectBad := 4

    if Plus(1, 2) != expectBad { // testcase miss
        t.Fatalf("Plus(%d, %d) should be %d, but doesn't match", 1, 2, 3)
    }

    t.Logf("[End %s]", t.Name())
}

func TestMinusGood(t *testing.T) {
    if testing.Short() {
        t.Skipf("skip %s!!", t.Name())
    }

    t.Logf("[Start %s]", t.Name())

    expect := 2

    if Minus(5, 3) != expect {
        t.Errorf("Minus(%d, %d) should be %d, but doesn't match", 5, 3, expect)
    }

    t.Logf("[End %s]", t.Name())
}

func TestMinusBad(t *testing.T) {
    if testing.Short() {
        t.Skipf("skip %s!!", t.Name())
    }

    t.Logf("[Start %s]", t.Name())

    expectBad := 3

    if Minus(5, 3) != expectBad { // testcase miss
        t.Errorf("Minus(%d, %d) should be %d, but doesn't match", 5, 3, 2)
    }

    t.Logf("[End %s]", t.Name())
}

いきなり量が増えましたが、少しずつ見ていきます。それぞれ、成功するテスト、失敗するテストの2つずつを用意しています。

足し算の方は、こんな感じ。

func TestPlusGood(t *testing.T) {
    t.Logf("[Start %s]", t.Name())

    expect := 3

    if Plus(1, 2) != expect {
        t.Fatalf("Plus(%d, %d) should be %d, but doesn't match", 1, 2, expect)
    }

    t.Logf("[End %s]", t.Name())
}

func TestPlusBad(t *testing.T) {
    t.Logf("[Start %s]", t.Name())

    expectBad := 4

    if Plus(1, 2) != expectBad { // testcase miss
        t.Fatalf("Plus(%d, %d) should be %d, but doesn't match", 1, 2, 3)
    }

    t.Logf("[End %s]", t.Name())
}

Logfというのは、Logに指定のフォーマットにしたものですね。Nameは、テストの名前を返します。

Fatalfというのは、フォーマットされたメッセージを与えつつテストを終了させるメソッドです。Fatalもあり、こちらは
単にメッセージを与えます。

func (*T) Fatalf

引き算の方はFatalfをErrorfに変更して、あとShortを使っています。

func TestMinusGood(t *testing.T) {
    if testing.Short() {
        t.Skipf("skip %s!!", t.Name())
    }

    t.Logf("[Start %s]", t.Name())

    expect := 2

    if Minus(5, 3) != expect {
        t.Errorf("Minus(%d, %d) should be %d, but doesn't match", 5, 3, expect)
    }

    t.Logf("[End %s]", t.Name())
}

func TestMinusBad(t *testing.T) {
    if testing.Short() {
        t.Skipf("skip %s!!", t.Name())
    }

    t.Logf("[Start %s]", t.Name())

    expectBad := 3

    if Minus(5, 3) != expectBad { // testcase miss
        t.Errorf("Minus(%d, %d) should be %d, but doesn't match", 5, 3, 2)
    }

    t.Logf("[End %s]", t.Name())
}

現時点で、このようなディレクトリ構成になっています。

$ tree
.
├── go.mod
├── main.go
├── main_test.go
└── sample
    ├── calc.go
    └── calc_test.go

では、テストを実行してみます。

$ go test
PASS
ok      testing-getting-started 0.002s


$ go test -v
=== RUN   TestSample
    main_test.go:8: Hello Testing!!
--- PASS: TestSample (0.00s)
PASS
ok      testing-getting-started 0.001s

どうやら、単にgo testとするだけではカレントディレクトリしか見てくれないようです。

そこでgo testの後に./...を付けると、サブディレクトリも見てくれるようになります。

ok    testing-getting-started (cached)
--- FAIL: TestPlusBad (0.00s)
    calc_test.go:20: [Start TestPlusBad]
    calc_test.go:25: Plus(1, 2) should be 3, but doesn't match
--- FAIL: TestMinusBad (0.00s)
    calc_test.go:52: [Start TestMinusBad]
    calc_test.go:57: Minus(5, 3) should be 2, but doesn't match
    calc_test.go:60: [End TestMinusBad]
FAIL
FAIL    testing-getting-started/sample  0.002s
FAIL


## -v付き
$ go test ./... -v
=== RUN   TestSample
    main_test.go:8: Hello Testing!!
--- PASS: TestSample (0.00s)
PASS
ok      testing-getting-started (cached)
=== RUN   TestPlusGood
    calc_test.go:8: [Start TestPlusGood]
    calc_test.go:16: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)
=== RUN   TestPlusBad
    calc_test.go:20: [Start TestPlusBad]
    calc_test.go:25: Plus(1, 2) should be 3, but doesn't match
--- FAIL: TestPlusBad (0.00s)
=== RUN   TestMinusGood
    calc_test.go:36: [Start TestMinusGood]
    calc_test.go:44: [End TestMinusGood]
--- PASS: TestMinusGood (0.00s)
=== RUN   TestMinusBad
    calc_test.go:52: [Start TestMinusBad]
    calc_test.go:57: Minus(5, 3) should be 2, but doesn't match
    calc_test.go:60: [End TestMinusBad]
--- FAIL: TestMinusBad (0.00s)
FAIL
FAIL    testing-getting-started/sample  0.002s
FAIL

なんとなく-vは./...の前に書きたい気もするのですが、この後いろいろフラグを書いていくと、パスは最初に書いた方がいいなぁと
思ってきました(書き換えるのはフラグの方が多そう)。

$ go test ./... -v

-vの方を見るとわかりますが、カレントディレクトリ+サブディレクトリになっていますね。

また、ErrorとFatal(使っているのはErrorfとFatalfですが)の違いもわかります。

足し算で失敗する方のテストはFatalfを使っていますが、Endが出力されていません。引き算の方はErrorfを使っていてEndまで
出力されています。

--- FAIL: TestPlusBad (0.00s)
    calc_test.go:20: [Start TestPlusBad]
    calc_test.go:25: Plus(1, 2) should be 3, but doesn't match
--- FAIL: TestMinusBad (0.00s)
    calc_test.go:52: [Start TestMinusBad]
    calc_test.go:57: Minus(5, 3) should be 2, but doesn't match
    calc_test.go:60: [End TestMinusBad]

つまり、ErrorはLog+Fail、FatalはLog+FailNowです。説明にもそのように書いています。

func (*T) Error

func (*T) Fatal

あとは、Shortですね。これは、-shortフラグの指定有無を確認します。

func Short

引き算のテストは、Shortがtrueを返すとテストをスキップするように書いているので-short付きで実行すると、テストが
実行されません。

$ go test ./... -short
ok      testing-getting-started (cached)
--- FAIL: TestPlusBad (0.00s)
    calc_test.go:20: [Start TestPlusBad]
    calc_test.go:25: Plus(1, 2) should be 3, but doesn't match
FAIL
FAIL    testing-getting-started/sample  0.002s
FAIL

-v付きで見ると、スキップされているのが確認できます。

$ go test ./... -short -v
=== RUN   TestSample
    main_test.go:8: Hello Testing!!
--- PASS: TestSample (0.00s)
PASS
ok      testing-getting-started (cached)
=== RUN   TestPlusGood
    calc_test.go:8: [Start TestPlusGood]
    calc_test.go:16: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)
=== RUN   TestPlusBad
    calc_test.go:20: [Start TestPlusBad]
    calc_test.go:25: Plus(1, 2) should be 3, but doesn't match
--- FAIL: TestPlusBad (0.00s)
=== RUN   TestMinusGood
    calc_test.go:33: skip TestMinusGood!!
--- SKIP: TestMinusGood (0.00s)
=== RUN   TestMinusBad
    calc_test.go:49: skip TestMinusBad!!
--- SKIP: TestMinusBad (0.00s)
FAIL
FAIL    testing-getting-started/sample  0.001s
FAIL

サブテストをまとめる

テスト用の関数内で、T#Runを呼び出すことでサブテストを作り、まとめることができます。

Subtests and Sub-benchmarks

Using Subtests and Sub-benchmarks - The Go Blog

calc_test.goに、こんな感じで追加してみます。

func TestGrouped(t *testing.T) {
    t.Logf("[Group Test Start %s]", t.Name())

    t.Run("TestPlus", func(t *testing.T) {
        t.Logf("[Start %s]", t.Name())

        expect := 3

        if Plus(1, 2) != expect {
            t.Fatalf("Plus(%d, %d) should be %d, but doesn't match", 1, 2, expect)
        }

        t.Logf("[End %s]", t.Name())
    })

    t.Run("TestMinus", func(t *testing.T) {
        if testing.Short() {
            t.Skipf("skip %s!!", t.Name())
        }

        t.Logf("[Start %s]", t.Name())

        expect := 2

        if Minus(5, 3) != expect {
            t.Errorf("Minus(%d, %d) should be %d, but doesn't match", 5, 3, expect)
        }

        t.Logf("[End %s]", t.Name())
    })

    t.Logf("[Group Test End %s]", t.Name())
}

T#Runの引数としてサブテストの名前と、テストを行う関数を渡します。ここでは、テストを行う関数はその場で定義しています。

確認。

$ go test ./... -v
=== RUN   TestSample
    main_test.go:8: Hello Testing!!
--- PASS: TestSample (0.00s)
PASS
ok      testing-getting-started (cached)
=== RUN   TestPlusGood
    calc_test.go:8: [Start TestPlusGood]
    calc_test.go:16: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)
=== RUN   TestPlusBad
    calc_test.go:20: [Start TestPlusBad]
    calc_test.go:25: Plus(1, 2) should be 3, but doesn't match
--- FAIL: TestPlusBad (0.00s)
=== RUN   TestMinusGood
    calc_test.go:36: [Start TestMinusGood]
    calc_test.go:44: [End TestMinusGood]
--- PASS: TestMinusGood (0.00s)
=== RUN   TestMinusBad
    calc_test.go:52: [Start TestMinusBad]
    calc_test.go:57: Minus(5, 3) should be 2, but doesn't match
    calc_test.go:60: [End TestMinusBad]
--- FAIL: TestMinusBad (0.00s)
=== RUN   TestGrouped
    calc_test.go:64: [Group Test Start TestGrouped]
=== RUN   TestGrouped/TestPlus
    calc_test.go:67: [Start TestGrouped/TestPlus]
    calc_test.go:75: [End TestGrouped/TestPlus]
=== RUN   TestGrouped/TestMinus
    calc_test.go:83: [Start TestGrouped/TestMinus]
    calc_test.go:91: [End TestGrouped/TestMinus]
=== CONT  TestGrouped
    calc_test.go:94: [Group Test End TestGrouped]
--- PASS: TestGrouped (0.00s)
    --- PASS: TestGrouped/TestPlus (0.00s)
    --- PASS: TestGrouped/TestMinus (0.00s)
FAIL
FAIL    testing-getting-started/sample  0.002s
FAIL

ここですね。

=== CONT  TestGrouped
    calc_test.go:94: [Group Test End TestGrouped]
--- PASS: TestGrouped (0.00s)
    --- PASS: TestGrouped/TestPlus (0.00s)
    --- PASS: TestGrouped/TestMinus (0.00s)

ちょっとわかりづらいですね。-runを使って実行するテストを絞り込んでみます。

サブテストを含むものだけ実行してみます。

$ go test ./... -run TestGrouped -v
testing: warning: no tests to run
PASS
ok      testing-getting-started 0.004s [no tests to run]
=== RUN   TestGrouped
    calc_test.go:64: [Group Test Start TestGrouped]
=== RUN   TestGrouped/TestPlus
    calc_test.go:67: [Start TestGrouped/TestPlus]
    calc_test.go:75: [End TestGrouped/TestPlus]
=== RUN   TestGrouped/TestMinus
    calc_test.go:83: [Start TestGrouped/TestMinus]
    calc_test.go:91: [End TestGrouped/TestMinus]
=== CONT  TestGrouped
    calc_test.go:94: [Group Test End TestGrouped]
--- PASS: TestGrouped (0.00s)
    --- PASS: TestGrouped/TestPlus (0.00s)
    --- PASS: TestGrouped/TestMinus (0.00s)
PASS
ok      testing-getting-started/sample  0.002s

サブテストを指定して実行することもできます。

$ go test ./... -run TestGrouped/TestPlus -v
testing: warning: no tests to run
PASS
ok      testing-getting-started 0.002s [no tests to run]
=== RUN   TestGrouped
    calc_test.go:64: [Group Test Start TestGrouped]
=== RUN   TestGrouped/TestPlus
    calc_test.go:67: [Start TestGrouped/TestPlus]
    calc_test.go:75: [End TestGrouped/TestPlus]
=== CONT  TestGrouped
    calc_test.go:94: [Group Test End TestGrouped]
--- PASS: TestGrouped (0.00s)
    --- PASS: TestGrouped/TestPlus (0.00s)
PASS
ok      testing-getting-started/sample  0.002s

通常のテストを指定することも可能ですし

$ go test ./... -run TestPlusGood -v
testing: warning: no tests to run
PASS
ok      testing-getting-started (cached) [no tests to run]
=== RUN   TestPlusGood
    calc_test.go:8: [Start TestPlusGood]
    calc_test.go:16: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)
PASS
ok      testing-getting-started/sample  (cached)

前方一致や

$ go test ./... -run TestPlus -v
testing: warning: no tests to run
PASS
ok      testing-getting-started 0.002s [no tests to run]
=== RUN   TestPlusGood
    calc_test.go:8: [Start TestPlusGood]
    calc_test.go:16: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)
=== RUN   TestPlusBad
    calc_test.go:20: [Start TestPlusBad]
    calc_test.go:25: Plus(1, 2) should be 3, but doesn't match
--- FAIL: TestPlusBad (0.00s)
FAIL
FAIL    testing-getting-started/sample  0.003s
FAIL

正規表現の指定も可能です。

$ go test ./... -run 'Test(Plus|Minus)Good' -v
testing: warning: no tests to run
PASS
ok      testing-getting-started 0.001s [no tests to run]
=== RUN   TestPlusGood
    calc_test.go:8: [Start TestPlusGood]
    calc_test.go:16: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)
=== RUN   TestMinusGood
    calc_test.go:36: [Start TestMinusGood]
    calc_test.go:44: [End TestMinusGood]
--- PASS: TestMinusGood (0.00s)
PASS
ok      testing-getting-started/sample  0.002s

TestMain

TestMainを使うことで、パッケージ内のテストを始める前後になにかしらの処理を挟み込むことができます。

Main

type M

テスト関数単位ではなく、パッケージ単位です。

試してみましょう。importを追加。

import (
    "fmt"
    "os"
    "testing"
)

TestMainという名前で、Mを引数に取る関数を定義します。TestMain以外の名前にしてはいけません。

func TestMain(m *testing.M) {
    fmt.Println("[Start TestMain]")
 
    ret := m.Run()

    fmt.Printf("[End TestMain] ret = %d\n", ret)

    os.Exit(ret)
}

また、M#Runの呼び出しを忘れると、そのパッケージ内のテストが実行されないことにも注意です。

func (*M) Run

Tが使えないのでLogも使えません。今回はfmt#Printlnを使ってログ出力し、この時にM#Runの戻り値を出力してみましょう。

確認。

$ go test ./... -v
=== RUN   TestSample
    main_test.go:8: Hello Testing!!
--- PASS: TestSample (0.00s)
PASS
ok      testing-getting-started (cached)
[Start TestMain]
=== RUN   TestPlusGood
    calc_test.go:10: [Start TestPlusGood]
    calc_test.go:18: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)
=== RUN   TestPlusBad
    calc_test.go:22: [Start TestPlusBad]
    calc_test.go:27: Plus(1, 2) should be 3, but doesn't match
--- FAIL: TestPlusBad (0.00s)
=== RUN   TestMinusGood
    calc_test.go:38: [Start TestMinusGood]
    calc_test.go:46: [End TestMinusGood]
--- PASS: TestMinusGood (0.00s)
=== RUN   TestMinusBad
    calc_test.go:54: [Start TestMinusBad]
    calc_test.go:59: Minus(5, 3) should be 2, but doesn't match
    calc_test.go:62: [End TestMinusBad]
--- FAIL: TestMinusBad (0.00s)
=== RUN   TestGrouped
    calc_test.go:66: [Group Test Start TestGrouped]
=== RUN   TestGrouped/TestPlus
    calc_test.go:69: [Start TestGrouped/TestPlus]
    calc_test.go:77: [End TestGrouped/TestPlus]
=== RUN   TestGrouped/TestMinus
    calc_test.go:85: [Start TestGrouped/TestMinus]
    calc_test.go:93: [End TestGrouped/TestMinus]
=== CONT  TestGrouped
    calc_test.go:96: [Group Test End TestGrouped]
--- PASS: TestGrouped (0.00s)
    --- PASS: TestGrouped/TestPlus (0.00s)
    --- PASS: TestGrouped/TestMinus (0.00s)
FAIL
[End TestMain] ret = 1
FAIL    testing-getting-started/sample  0.002s
FAIL

よーく見ると、パッケージ内のテスト開始前後にログが追加されています。

[Start TestMain]
=== RUN   TestPlusGood
    calc_test.go:10: [Start TestPlusGood]
    calc_test.go:18: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)

...

=== CONT  TestGrouped
    calc_test.go:96: [Group Test End TestGrouped]
--- PASS: TestGrouped (0.00s)
    --- PASS: TestGrouped/TestPlus (0.00s)
    --- PASS: TestGrouped/TestMinus (0.00s)
FAIL
[End TestMain] ret = 1

トップレベルの方に変化はありませんね。TestMainがないからです。

=== RUN   TestSample
    main_test.go:8: Hello Testing!!
--- PASS: TestSample (0.00s)
PASS
ok      testing-getting-started (cached)

戻り値に関してですが、成功するテストに絞ってみましょう。

$ go test ./... -run 'Test(Plus|Minus)Good' -v
testing: warning: no tests to run
PASS
ok      testing-getting-started (cached) [no tests to run]
[Start TestMain]
=== RUN   TestPlusGood
    calc_test.go:10: [Start TestPlusGood]
    calc_test.go:18: [End TestPlusGood]
--- PASS: TestPlusGood (0.00s)
=== RUN   TestMinusGood
    calc_test.go:38: [Start TestMinusGood]
    calc_test.go:46: [End TestMinusGood]
--- PASS: TestMinusGood (0.00s)
PASS
[End TestMain] ret = 0
ok      testing-getting-started/sample  (cached)

戻り値が0になりました。

[End TestMain] ret = 0

とりあえず、こんなところでしょうか。

オマケ

最後に、ディレクトリツリーと書いたテストコードの全体を載せておきます。

$ tree
.
├── go.mod
├── main.go
├── main_test.go
└── sample
    ├── calc.go
    └── calc_test.go

sample/calc_test.go

package sample

import (
    "fmt"
    "os"
    "testing"
)

func TestPlusGood(t *testing.T) {
    t.Logf("[Start %s]", t.Name())

    expect := 3

    if Plus(1, 2) != expect {
        t.Fatalf("Plus(%d, %d) should be %d, but doesn't match", 1, 2, expect)
    }

    t.Logf("[End %s]", t.Name())
}

func TestPlusBad(t *testing.T) {
    t.Logf("[Start %s]", t.Name())

    expectBad := 4

    if Plus(1, 2) != expectBad { // testcase miss
        t.Fatalf("Plus(%d, %d) should be %d, but doesn't match", 1, 2, 3)
    }

    t.Logf("[End %s]", t.Name())
}

func TestMinusGood(t *testing.T) {
    if testing.Short() {
        t.Skipf("skip %s!!", t.Name())
    }

    t.Logf("[Start %s]", t.Name())

    expect := 2

    if Minus(5, 3) != expect {
        t.Errorf("Minus(%d, %d) should be %d, but doesn't match", 5, 3, expect)
    }

    t.Logf("[End %s]", t.Name())
}

func TestMinusBad(t *testing.T) {
    if testing.Short() {
        t.Skipf("skip %s!!", t.Name())
    }

    t.Logf("[Start %s]", t.Name())

    expectBad := 3

    if Minus(5, 3) != expectBad { // testcase miss
        t.Errorf("Minus(%d, %d) should be %d, but doesn't match", 5, 3, 2)
    }

    t.Logf("[End %s]", t.Name())
}

func TestGrouped(t *testing.T) {
    t.Logf("[Group Test Start %s]", t.Name())

    t.Run("TestPlus", func(t *testing.T) {
        t.Logf("[Start %s]", t.Name())

        expect := 3

        if Plus(1, 2) != expect {
            t.Fatalf("Plus(%d, %d) should be %d, but doesn't match", 1, 2, expect)
        }

        t.Logf("[End %s]", t.Name())
    })

    t.Run("TestMinus", func(t *testing.T) {
        if testing.Short() {
            t.Skipf("skip %s!!", t.Name())
        }

        t.Logf("[Start %s]", t.Name())

        expect := 2

        if Minus(5, 3) != expect {
            t.Errorf("Minus(%d, %d) should be %d, but doesn't match", 5, 3, expect)
        }

        t.Logf("[End %s]", t.Name())
    })

    t.Logf("[Group Test End %s]", t.Name())
}

func TestMain(m *testing.M) {
    fmt.Println("[Start TestMain]")
 
    ret := m.Run()

    fmt.Printf("[End TestMain] ret = %d\n", ret)

    os.Exit(ret)
}