ReadMeFirst
go1.7
からSubTestsが使えるようになったのだけど、
使っていなかったので、調べがてら、その紹介とメモ。
あと、ついでにTableDrivenTestについて書いておく。
Subtestsとは
Subtestsの追加によって、テストに階層を作ることができるようになりました。
Runメソッドを使ってテスト内に小さなテストを書いてみます。
コード例
- example.go
package main
import "fmt"
func Example(name string) string {
if name == "" {
return "blank name"
}
return fmt.Sprintf("Hello %s", name)
}
- example_test.go
func TestExample(t *testing.T) {
t.Run("case Empty", func(t *testing.T) {
result := Example("")
expected := "blank name"
if expected != result {
t.Fatalf("failed Test")
}
})
t.Run("case Normal", func(t *testing.T) {
result := Example("John")
expected := "Hello John"
if expected != result {
t.Fatalf("failed Test")
}
})
}
t.Run内でPararell()を使った並行テストも可能です。
実行の仕方
サブテストの含まれるテスト関数を実行すれば良いだけです。
ドキュメントを見る限り、以下のような、オプションによるマッチングによって特定のサブテスト
だけを実行することも可能です。
go test -run Foo/A= # For top-level tests matching "Foo", run subtests matching "A=".
Fooを含むトップレベルテスト(通常のテスト関数)内でA=を含むサブテストを実行する。
go test -run /A=1 # For all top-level tests, run subtests matching "A=1".
A=1を含むサブテストを実行する。
実行結果
$ go test ./...
--- FAIL: TestExample (0.00s)
--- FAIL: TestExample/case_Empty (0.00s)
hoge_test.go:12: failed Test
--- FAIL: TestExample/case_Normal (0.00s)
hoge_test.go:19: failed Test
失敗させると。第1引数で指定した名前を出してくれますね。
Benchmarkでも使える。
testing.Bでも使えますので、Benchmarkについても階層化が可能です。
こちらはまぁ便利な気もする。
SubTestの使いどころ
正直、個人的にはこれを待っていたよ!
って感動を覚えるものではありませんでした。
というのも、階層が必要なほど複雑なテストが必要な場合はテスト対象コードの仕事が多すぎる
気がしますし
1Test1Assertionで育ったこともあり、テスト関数自体を分けてしまうスタンスでしたので。
GoではErrorのような、処理自体は継続する事ができるので、Assertion Roulete
のような事はないと
思うのですが、一つのテスト関数に複数を盛り込んで長くするのが、そもそも苦手なのもあり。
うーん。すごく複雑なテストケースが必要な場合もあるのかもしれないし、
見てみたさがあります。求む、エレガントな使用ケース (切実)
TableDrivenTestで大体は十分
上の例程度のケースならgolangの推奨するTableDrivenTestでも可能です。
TableDrivenTestは入力値
、出力値
で構成するデータを用意して
iterateでテストを回す感じがコードも短く済むので、推奨されてます。
go公式から引用ですが。
var flagtests = []struct {
in string
out string
}{
{"%a", "[%a]"},
{"%-a", "[%-a]"},
{"%+a", "[%+a]"},
{"%#a", "[%#a]"},
{"% a", "[% a]"},
{"%0a", "[%0a]"},
{"%1.2a", "[%1.2a]"},
{"%-1.2a", "[%-1.2a]"},
{"%+1.2a", "[%+1.2a]"},
{"%-+1.2a", "[%+-1.2a]"},
{"%-+1.2abc", "[%+-1.2a]bc"},
{"%-1.2abc", "[%-1.2a]bc"},
}
func TestFlagParser(t *testing.T) {
var flagprinter flagPrinter
for _, tt := range flagtests {
s := Sprintf(tt.in, &flagprinter)
if s != tt.out {
t.Errorf("Sprintf(%q, &flagprinter) => %q, want %q", tt.in, s, tt.out)
}
}
}
要するにphpunitにあったDataProvider
をfor文とErrorf
で自力でやる感じです。
個人的にはメッセージも各パターン別にある方が親切かな。。。と。
{"input-a","out-a","message at failed"},
{"input-b","out-b","message at failed"},
{"input-c","out-c","message at failed"},
{"input-d","out-d","message at failed"},
とか。