はじめに
Goが推奨するtable-driven testが、Cでも記述できるのに気づきました。
Goのtable-driven test
Goのテストは、table-driven testが推奨されています。テスト対象に与えるデータ入力と期待値のペアを1つのテストケースとして扱い、期待値と実測値の一覧を表のように表します。
たとえばgcd()
メソッドのテストをtable-drivenで書くなら、次のようになります。
func TestGcd(t *testing.T) {
cases := []struct {
a, b int
z int
}{
{12, 8, 4},
{3, 7, 1},
}
for _, c := range cases {
if gcd(c.a, c.b) != c.z {
t.Errorf("expected gcd(%v,%v) == %v\n", c.a, c.b, c.z)
}
}
}
Goはstructの定義と変数の初期化を同時に書くことができます。テストケースの初期化はstruct literal syntax{}
で、容易かつ型安全に初期化できます。コードはテストデータのみに集中できるので、非常に読みやすいです。
Cのtable-driven test
Cで同じくgcd()
のテストをtable-drivenに記述してみました。Cでも構造体宣言と変数の初期化が同時に行えます。そして初期化はstruct literal syntax{}
で型安全に行うことができます。
# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
int test_gcd() {
struct {
int a, b;
int z;
} cases[] = {
{12, 8, 4},
{3, 7, 1},
};
for (int i = 0; i < ARRAY_SIZE(cases); ++i) {
if (gcd(cases[i].a, cases[i].b) != cases[i].z) {
printf("expected gcd(%d,%d) == %d\n", cases[i].a, cases[i].b, cases[i].z);
}
}
}
Goと比較しても、記述量や可読性に大きな差がなく、C言語でtable-driven testを記述できました。
おわりに
Goは簡単なCとよく言われますが、Goの文化をCに輸入するのもよいですね。