⛳
GolangのTable Driven Testとt.Parallel()には罠がある
概要
テストの各条件を明記して
あとは同じような処理でループ回しちゃうTable Drivenな書き方は好きだけど
Testxxxとt.Run()をどちらもt.Parallel()
で並行処理させると
想定外の挙動してしまうよね。っていうお話
想定外の動き
こんな感じでTable Drivenなテストを書いてみる
package main
import (
"fmt"
"testing"
)
func TestSomething(t *testing.T) {
t.Parallel()
tests := []struct{
name string
value int
}{
{
name: "test1",
value: 1,
},
{
name: "test2",
value: 2,
},
{
name: "test3",
value: 3,
},
}
for _, tt:= range tests {
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
fmt.Println(fmt.Sprintf("%+v", tt.value))
})
}
}
1, 2, 3が出力されてくれるかと思いきや...
$ go test -v
=== RUN TestSomething
=== PAUSE TestSomething
=== CONT TestSomething
=== RUN TestSomething/test1
=== PAUSE TestSomething/test1
=== RUN TestSomething/test2
=== PAUSE TestSomething/test2
=== RUN TestSomething/test3
=== PAUSE TestSomething/test3
=== CONT TestSomething/test1
3
=== CONT TestSomething/test3
3
=== CONT TestSomething/test2
3
--- PASS: TestSomething (0.00s)
--- PASS: TestSomething/test1 (0.00s)
--- PASS: TestSomething/test3 (0.00s)
--- PASS: TestSomething/test2 (0.00s)
PASS
3しか出てこない!!なんでや!!!
原因
この辺の影響みたい
loop内でgoroutineするとうまくfor内のvalueが渡されないケースがあるみたいで
そいつに引っかかるらしい
確かに、ポインターとか絡めてループすると
なんか意図したループにならないケースあったけどこの辺の影響だったのか・・・
解決法
ループ内で変数を再宣言する
これtt := tt
for _, tt:= range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
t.Parallel()
ちゃんと全部でた🤩
$ go test -v
=== RUN TestSomething
=== PAUSE TestSomething
=== CONT TestSomething
=== RUN TestSomething/test1
=== PAUSE TestSomething/test1
=== RUN TestSomething/test2
=== PAUSE TestSomething/test2
=== RUN TestSomething/test3
=== PAUSE TestSomething/test3
=== CONT TestSomething/test1
1
=== CONT TestSomething/test3
3
=== CONT TestSomething/test2
2
--- PASS: TestSomething (0.00s)
--- PASS: TestSomething/test1 (0.00s)
--- PASS: TestSomething/test3 (0.00s)
--- PASS: TestSomething/test2 (0.00s)
PASS
まとめ
「テスト全部通ってるやん😎」と思ったら
最後のケースしか実行されてなかった。
というだいぶえぐいハマり方をしたのでメモ残しとく
Discussion