この記事はAltplus Advent Calendar 2017の13日目のエントリです。
最近なぜかJava関連のアカウントにTwitterでフォローされることが多い、JavaマスコットのDukeを愛してやまない竹田です。
テーブル駆動テスト
今回は、Go wikiで紹介されて、Goコミュニティでよく使われている(?)テーブル駆動開発をC++でやってみたいと思います。
テーブル駆動テストとは、テストの入力と期待値をテーブルの行に記載し、テーブルを走査しながら実行していくテストのことです。
例えば、int型の入力を受け取って、2倍する関数int twice(int x)
のテスト・テーブルは以下のようになります。
in(入力) | out(出力) |
---|---|
0 | 0 |
1 | 2 |
2 | 4 |
このテーブルを使って、テーブル駆動テストを書くと以下のようになります。
#include <cassert>
int twice(int x){
return x * 2;
}
int main()
{
struct { int in; int out; } table[]{
{0, 0},
{1, 2},
{2, 4}
};
for(auto [in, out]: table){
assert(twice(in) == out);
}
}
テスト・ケース毎にAssertを書く必要がなくなり、テーブルに入出力がまとめられているため、関数の入出力仕様に集中することができます。入出力で仕様が決定される純粋な関数のテストに役立ちます。
関数の引数が増えてもテーブルの列を追加するだけで対応できます。文字列とインデックスを取って、インデックス番目の文字を返すat関数のテーブルは、
文字列(in1) | インデックス(in2) | 文字(out) |
---|---|---|
"abc" | 0 | 'a' |
"Hello" | 3 | 'l' |
"Hello world!" | 7 | 'r' |
#include <cassert>
#include <string_view>
char at(std::string_view s, int index){
return s[index];
}
int main()
{
struct { char const* str; int index; char out; } table[]{
{"abc", 0, 'a'},
{"Hello", 3, 'l'},
{"Hello world!", 7, 'o' }
};
for(auto [s, i, out]: table){
assert(at(s, i) == out);
}
}
最後に
テーブル駆動テストを利用すると、関数の入出力仕様にフォーカスしたテスト・ケースを見通しよく記載することができます。
C++の"unnamed struct"(テーブルの定義に利用している名前なしのstruct)、"range-based for"、"structured bindings"を利用して、快適なテーブル駆動テストにしましょう!