Go の Unit Testing に関してやりたいことを調べてみた - パラメータによるフィルタリングと並行性の制御

Go のプロジェクトでちょっと凝ったテスティングをする必要が生じたので、いろいろ調べ物をしたのでそのメモを残しておきたい。

# Go test のタイムアウトは？

こちらのページで、`go test` のフラグを確認することが出来る。丁寧にかいているのでわかりやすい。`-timeout d` で設定できるようだ。

> The default is 10 minutes (10m)

# Go test で、`t.Logf` を書いているのにログが出ない。

フラグの設定が必要。`-test.v` をつける。`-v` は元々 `Verbose output` の意味で、テストの場合は、`-test` のプレフィックスをつけるのが必須。

# パラメータ付きのテストをしたい

沢山の言語をサポートしている環境で、下記のようなことを実践したい。

* 各言語のテストは、パラレルで実行できない。シリアルでやりたい。
* 各エクステンションのテストは、パラレルで実行したい。
* 通常は全部のテストをランするが、テストの実行をエクステンション及び言語でフィルターしたい

上記のことを実現するために下記のことを調査した。

## シリアル・パラレルの切り替え

下記を書くことで、パラレル実行される。なければパラレル実行されない。

```go
t.Parallel()
```

## パラメータによる言語のフィルター

`t.Run` を使うと、フィルターをすることができる。

* [testing - The Go Programming Language](https://golang.org/pkg/testing/)

次のプログラムで、`t.Run` を使う。`t.Run` を設定すると、go routine でそれぞれの関数が実行される。そうすると、並行性が気になるところだが、`t.Parallel()` を書かない限り、並行では処理されない。つまり、このプログラムでは、言語ごとに、シリアルで、実行される。


```go:kafka_test.go
package main

import (
	"fmt"
	"testing"
	"time"
)

func TestKafkaTrigger(t *testing.T) {
	t.Parallel()
	t.Log("******* kafka testing")
	testCase := []string{
		"dotnet",
		"java",
	}

	for _, tc := range testCase {
		t.Run(fmt.Sprintf("Image=%s", tc), func(t *testing.T) {
			// t.Parallel()
			t.Logf("Test run not in parallel %s", tc)
			time.Sleep(3 * time.Second)
			t.Logf("Finish wating for bye 3 sec: %s", tc)
		})
	}

}
```

テストの実行時に何も指定しなければ、他のテストケース含めて全部実行される。

```bash
$ go test -test.v
=== RUN   TestKafkaTrigger
=== PAUSE TestKafkaTrigger
=== RUN   TestRabbitMQTriggerTest
=== PAUSE TestRabbitMQTriggerTest
=== CONT  TestKafkaTrigger
=== CONT  TestRabbitMQTriggerTest
    rabbitmq_test.go:7: ******* rabbitmq testing
--- PASS: TestRabbitMQTriggerTest (0.00s)
=== CONT  TestKafkaTrigger
    kafka_test.go:11: ******* kafka testing
=== RUN   TestKafkaTrigger/Image=dotnet
    kafka_test.go:20: Test run not in parallel dotnet
    kafka_test.go:22: Finish wating for bye 3 sec: dotnet
=== RUN   TestKafkaTrigger/Image=java
    kafka_test.go:20: Test run not in parallel java
    kafka_test.go:22: Finish wating for bye 3 sec: java
--- PASS: TestKafkaTrigger (6.00s)
    --- PASS: TestKafkaTrigger/Image=dotnet (3.00s)
    --- PASS: TestKafkaTrigger/Image=java (3.00s)
PASS
ok      simplearchitect.com     6.006s
```

## テストでフィルタする

`-run <Test Function regex>` でマッチする。

```bash
$ go test -test.v -run TestKafkaTrigger
=== RUN   TestKafkaTrigger
=== PAUSE TestKafkaTrigger
=== CONT  TestKafkaTrigger
    kafka_test.go:11: ******* kafka testing
=== RUN   TestKafkaTrigger/Image=dotnet
    kafka_test.go:20: Test run not in parallel dotnet
    kafka_test.go:22: Finish wating for bye 3 sec: dotnet
=== RUN   TestKafkaTrigger/Image=java
    kafka_test.go:20: Test run not in parallel java
    kafka_test.go:22: Finish wating for bye 3 sec: java
--- PASS: TestKafkaTrigger (6.01s)
    --- PASS: TestKafkaTrigger/Image=dotnet (3.00s)
    --- PASS: TestKafkaTrigger/Image=java (3.00s)
PASS
ok      simplearchitect.com     6.007s
```

例えばテストに次の関数を追加する。

```go:kafka_test.go
   :
func TestKafkaTriggerWithSomething(t *testing.T) {
	t.Parallel()
	t.Log("******* kafka testing something")
}
```

テストを指定すると前方一致でマッチするので、TestKafkaTriggerWithSomething も一緒にマッチする。

```bash
$ go test -test.v -run TestKafkaTrigger
=== RUN   TestKafkaTrigger
=== PAUSE TestKafkaTrigger
=== RUN   TestKafkaTriggerWithSomething
=== PAUSE TestKafkaTriggerWithSomething
=== CONT  TestKafkaTrigger
=== CONT  TestKafkaTriggerWithSomething
    kafka_test.go:30: ******* kafka testing something
=== CONT  TestKafkaTrigger
    kafka_test.go:11: ******* kafka testing
=== RUN   TestKafkaTrigger/Image=dotnet
    kafka_test.go:20: Test run not in parallel dotnet
--- PASS: TestKafkaTriggerWithSomething (0.00s)
=== CONT  TestKafkaTrigger/Image=dotnet
    kafka_test.go:22: Finish wating for bye 3 sec: dotnet
=== RUN   TestKafkaTrigger/Image=java
    kafka_test.go:20: Test run not in parallel java
    kafka_test.go:22: Finish wating for bye 3 sec: java
--- PASS: TestKafkaTrigger (6.00s)
    --- PASS: TestKafkaTrigger/Image=dotnet (3.00s)
    --- PASS: TestKafkaTrigger/Image=java (3.00s)
PASS
ok      simplearchitect.com     6.006s
```

防ぎたい場合は、`$` をつけて終端であることを示せばよい

```bash
$ go test -test.v -run TestKafkaTrigger$
=== RUN   TestKafkaTrigger
=== PAUSE TestKafkaTrigger
=== CONT  TestKafkaTrigger
    kafka_test.go:11: ******* kafka testing
=== RUN   TestKafkaTrigger/Image=dotnet
    kafka_test.go:20: Test run not in parallel dotnet
    kafka_test.go:22: Finish wating for bye 3 sec: dotnet
=== RUN   TestKafkaTrigger/Image=java
    kafka_test.go:20: Test run not in parallel java
    kafka_test.go:22: Finish wating for bye 3 sec: java
--- PASS: TestKafkaTrigger (6.00s)
    --- PASS: TestKafkaTrigger/Image=dotnet (3.00s)
    --- PASS: TestKafkaTrigger/Image=java (3.00s)
PASS
ok      simplearchitect.com     6.004s
```

# パラメータをテストする。

`dotnet` もしくは、`java` の言語のみテストしたい！という場合どうしようか？
その場合は次のようにしよう。きれいにフィルターされる。まさに思っていた挙動が簡単に実現した！

```bash
$ go test -test.v -run TestKafkaTrigger$/java
=== RUN   TestKafkaTrigger
=== PAUSE TestKafkaTrigger
=== CONT  TestKafkaTrigger
    kafka_test.go:11: ******* kafka testing
=== RUN   TestKafkaTrigger/Image=java
    kafka_test.go:20: Test run not in parallel java
    kafka_test.go:22: Finish wating for bye 3 sec: java
--- PASS: TestKafkaTrigger (3.00s)
    --- PASS: TestKafkaTrigger/Image=java (3.00s)
PASS
ok      simplearchitect.com     3.004s
```

# おまけ Struct オブジェクトの比較をテストの中でしたい。

cmp.Diff が便利。cmp.Equal もあるのだが、cmp.Diff の方が、テストが失敗したときにDiffが出るので何が違うのかわかって便利。

```go
assert.Empty(t, cmp.Diff(expectedFirstSuccess, ((*buildContextCollection).Collection)[0], cmpopts.IgnoreFields(expectedFirstSuccess, "Error")))

```

Go のテストはまだまだ奥深い。時間をとってしっかり学ぶべきやな。常にテスト駆動でコードが書けるようになりたい。