Goでテストコードを書く方法
入力に対して常に出力を伴うように実装する
==引数が常にある、戻り値が常にある
なるべく副作用の無い関数を書く
戻り値が無い関数は副作用がある
関数の行数は少なく、細かく分割する
テストの考え方
正常系、異常系、組み合わせ、0値、nil、マイナス値、最小値、最大値、境界値などを網羅的に検証する
正常系1パターンだけ、とかはNG
panicなどで異常終了しないこと、といったテストコードだけだとあまり意味がない
エラーが返ること、エラーが返らないこと、エラー型などを検証する
テスト用のソースファイル名は_test.goと命名しなければならない
テスト用の関数はfunc Test****(t *testing.T)と書く
code:main_test.go
func TestHoge(t *testing.T) {
// TODO...
}
関数名がTestから始まらないといけない
引数は*testing.なにがしでないといけない
よく使うのは*testing.Tと*testing.B(ベンチマーク用)
他にもあるのかは知らない
戻り値は返してはならない
戻り値を追加するとテストが実行できない
テストの実行はgo testと実行する
パッケージ配下のソースも全部テストしたい場合はgo test ./...と実行する
カバレッジ(網羅率)も確認したい場合はgo test -coverで実行する ただ、これだけだとテストコードを書くのがめんどくさい
ということで
標準パッケージのtestingに加えてtestify/assertを使う
assert.Equalなどの値比較を簡単にかけるようになる
使い方
go get github.com/stretchr/testifyでパッケージ追加
main.goのテスト用のソースで、自作のformat関数をテストしたい
code:main_test.go
type TestFormatData struct {
matrix [][]string
opts options
out []string
}
func TestFormat(t *testing.T) {
tds := []TestFormatData{
TestFormatData{
matrix: [][]string{
{"id", "name", "note"},
{"1", "taro", "hogehoge"},
{"2", "hanako", "foobar"},
},
opts: options{Delimiter: ","},
out: []string{
"id,name,note",
"1,taro,hogehoge",
"2,hanako,foobar",
},
},
TestFormatData{
matrix: [][]string{
{"id", "name", "note"},
{"1", "taro", "hogehoge"},
{"2", "", "foobar"},
},
opts: options{Delimiter: ","},
out: []string{
"id,name,note",
"1,taro,hogehoge",
"2,,foobar",
},
},
TestFormatData{
matrix: [][]string{
{"id", "name", "note"},
{"1", "taro", "hogehoge"},
{"2", "hanako", "foobar"},
},
opts: options{Delimiter: "\t"},
out: []string{
"id name note",
"1 taro hogehoge",
"2 hanako foobar",
},
},
TestFormatData{
matrix: [][]string{
{"id", "name", "note"},
{"1", "taro", "hogehoge"},
{"2", "hanako", "foobar"},
},
opts: options{Delimiter: ""},
out: []string{
"idnamenote",
"1tarohogehoge",
"2hanakofoobar",
},
},
TestFormatData{
matrix: [][]string{},
opts: options{Delimiter: ""},
out: []string{},
},
TestFormatData{
matrix: nil,
opts: options{Delimiter: ""},
out: []string{},
},
}
for _, v := range tds {
out := format(v.matrix, v.opts)
assert.Equal(t, v.out, out)
}
}
こんなかんじで、テスト用の構造体を定義して配列でぶん回して値検証をするコードをよく書く
panicが発生したりしなければ、ループの途中でエラーが発生しても最後までテストしてくれる
メリット
配列の値を追加するだけでよいので、テストパターンを増やしやすい
デメリット
いちいち構造体を定義しないといけない
まぁmain.goからは参照できないんで、実装するのに手間がかかるだけ