google-gtestのValue-Parameterized Testsが便利過ぎる件

google-gtestにいつの間にかValue-Parameterized Tests(VPTと勝手に略します)という機能が追加されていました。

以前のgtestでは、実行される度に特定の値を変更しながらテストすることが面倒でした。例えば、boolのフラグを持っており、それがtrueかfalseかで挙動が変わるようなケースです。そのようなテストケースを書きたかったら、次のようにヘルパ関数を用意するか、テストをコピペして値を変更するなどの処理を書く必要がありました。

TEST(HogeTest, hogehoge) {
  hogeHelper(true); // trueでテスト
  hogeHelper(false); // falseでテスト
}

前者では問題が発生した時に異なるテストが同じ行番号を出力するため、問題の把握がしづらくなります。また、TESTがたくさんあった場合、それぞれにヘルパ関数を用意する必要があり、非常に面倒です。後者は言うまでもなくメンテナンスが大変です。

そこで登場するのがVPTです。この機能を使うにはTEST_Fするときと同じような感じでクラスを用意します。TEST_Fのときにはtesting::Testを継承しますが、VPTの場合はtesting::TestWithParam<型>を継承します。型にはパラメタの型を指定します。

class HogeTest : public testing::TestWithParam<bool> {
  // testing::Testを継承したときと同じような感じ
};

testing::TestWithParamはtesting::Testの派生クラスであるため、HogeTestではSetUp/TearDown/SetUpTestCase/TearDownTestCaseなど、TEST_Fで使用できる関数を同じように利用できます。

次にテストケースの書き方ですが、VPTの場合はTEST_Pを使用します。TestWithParamで指定したパラメタはGetParam()で取得することができます。

TEST_P(HogeTest, hogehoge)
{
  GetParam()でboolの値が帰ってくるので、hogeHelperの中身をここに書くことができる
}

GetParamで与える値は、INSTANTIATE_TEST_CASE_Pを使って指定します。

INSTANTIATE_TEST_CASE_P(HogeTestHogeInstance,
                        HogeTest,
                        testing::Values(true, false));

このようにすると、定義したすべてのTEST_Pに対してtrue,false両方の値でテストが実行されます。

値の部分にはRange, Values, ValuesIn, Combineなどを指定できるらしいです。詳しくはドキュメントを参照してください。例えばValuesInにはSTLのコンテナが渡せるので、

set<string> Parameters();
INSTANTIATE_TEST_CASE_P(AbabababaInstance,
                        AbabababaTest,
                        testing::ValuesIn(Parameters()));

のようなこともできます。手動で値を列挙するのが大変そうな場合に便利です。今回は4つの要素を持つ集合のべき集合-空集合なものに対してそれぞれテストを実行したかったので、かなり助かりました。ソースを読んでないので使い方は良く分かりませんが、Combineを使うと集合の直積を作ってくれるらしいので、それだけでもかなりいろいろなことができるんじゃないでしょうか。

ちなみに、Valuesなどに渡す値はSetUpTestCaseよりも前に評価されてしまうので、少し複雑な値を用意したいときなどは上記のようにグローバル/ファイルスコープな関数を一個はさんであげるとよさそうです。その場合、SetUpTestCaseが呼ばれる前に何度かその関数が呼び出されるようなので、値の生成に時間がかかるような場合はstatic boolなローカル変数を用意して、一度だけ値の生成を行うような実装にしておくといいかもしれません。SetUpTestCase以降は余分な呼び出しは起こりませんでした。