📌
Table Driven Tests
Table Driven Tests
前回の記事の続きとなります。
GoではTable Driven Testsを推奨しています。難しそうな名前が付いていますが、これは単純にテストケースをテーブルとしてまとめたものです。
sample.go
package hsd
func StringDistance(lhs,rhs string) int {
return Ditance([]rune(lhs),[]rune(rhs))
}
func Ditance(a,b []rune) int {
dist := 0
if len(a) != len(b) {
return -1
}
for i := range a {
if a[i] != b[i] {
dist++
}
}
return dist
}
sample_test.go
package hsd
import (
"reflect"
"testing"
)
func TestStringDistance(t *testing.T) {
tests := []struct {
name string
lhs string
rhs string
want int
}{
{name: "lhs is longer than rhs", lhs: "foo", rhs: "fo", want: -1},
{name: "lhs is shorter than rhs", lhs: "fo", rhs: "foo", want: -1},
{name: "No diff", lhs: "foo", rhs: "foo", want: 0},
{name: "1 diff", lhs: "foo", rhs: "foh", want: 1},
{name: "2 diffs", lhs: "foo", rhs: "fhh", want: 2},
{name: "3 diffs", lhs: "foo", rhs: "hhh", want: 3},
{name: "multibyte", lhs: "あいう", rhs: "あいえ", want: 1},
}
for _, tc := range tests {
got := StringDistance(tc.lhs,tc.rhs)
if !reflect.DeepEqual(tc.want,got) {
t.Fatalf("%s: expected: %v,got: %v", tc.name, tc.want, got)
}
}
}
↓実行結果
terminal
go-test % go test
PASS
ok example.com 0.469s
このように、テーブルで入力条件と期待値をまとめておくことでテストの抜けを見つけやすくなり、テストを追加するのも簡単になります。また網羅性もよくなります。
テーブルに持たせる項目はおおよそ以下になります。
- テストの名前(name)
- 入力(この場合はlhsやrhs)
- 期待値(want)
特にテスト名(name)は重要です。テーブルが多くなると、どのテストで失敗したかわからなくなります。
もちろん、すべてのコードがTable Driven Testsでテストできるわけではありません。境界値チェックやバリエーションチェックが必要な関数に向いています。
このようにテストをしっかりと書いておくことで、以降リファクタリングを行った際に、その変更が間違ったものであることにいち早く気づくことができます。このことが「安全に壊す」なのです。なおリファクタリングする前には必ず、git commit
しておきましょう。
Discussion
Gherkin記法の
Examples
みたいな感じなんですかね。コメントありがとうございます!
記事の方拝見させていただきました。
確かに似てますね!