忙しい人のための結論
Go はそもそもテストコードを書くのにテスティングフレームワークを使わないのが文化として根付いているし、パラメタライズドテストも普通にループ処理で実現する。
Go でパラメタライズドテスト
先日、TDDBC 仙台 5 に行ってきたのだけど、その課題に Go で挑戦してみたときにテストをパラメタライズしたくなった。
で、それってどうやるんですかってTwitterで聞いてみたら、どうやら(予想通りと言えば予想通り)普通にそのための構造体を作ってループで処理するしかないっぽい。
つまりこんな感じになる。
// test code
func TestAdd(t *testing.T) {
params := []struct {
a int
b int
expected int
}{
{ a: 1, b: 1, expected: 2 },
{ a: 1, b: 2, expected: 3 },
{ a: 2, b: -3, expected: -1 },
}
for _, p := range params {
expected := p.expected
actual := Add(p.a, p.b)
if p.expected != actual {
t.Errorf("Expected: %v, Actual: %v", expected, actual)
}
}
}
// product code
func Add(a int, b int) int {
return a + b
}
途中で落ちた場合
多くのテスティングフレームワークでは 1 テストケース 1 アサーションが推奨されていて、テストケース内にアサーションが複数あった場合に前のアサーションが失敗するとそれ以降のアサーションは評価されない。
上記の Go のテストでも途中で落ちた場合にそのような挙動になるとあまり嬉しくないのだけど実際はどうなるのか。
こんな感じに変更して試してみた。
params := []struct {
a int
b int
expected int
}{
{ a: 1, b: 1, expected: 2 },
{ a: 1, b: 2, expected: 2 }, // miss!
{ a: 2, b: -3, expected: 1 }, // miss!
}
結果はこんな感じ。
--- FAIL: TestAdd (0.00s)
sum_test.go:24: Expected: 2, Actual: 3
sum_test.go:24: Expected: 1, Actual: -1
FAIL
exit status 1
FAIL sum_test 0.007s
2 つ目のテストケースで終わらず 3 つ目のテストまで実行され 2 つとも落ちている。
t.Errorf
内のメッセージやその一部をパラメータに含める等の工夫をすればテスト失敗時の情報量もリッチにできるし、パラメタライズドテストとして問題なく使っていけそうな気がする。