kkAyatakaのメモ帳。

誰かの役に立つかもしれない備忘録。

Google Testでデータドリブンテスト

他の (言語の) テスティングフレームワークで、同一のテストを異なるデータで実行するデータドリブンテストの機能があって、これGoogle Testでも無いんかいな、と調べたところあった。

Google Testでは「Value-Parameterized Tests」として説明されている。説明内に「data-driven testing」って記述があるので、検索すれば引っかかる。

Value-Parameterized Tests

使用する機能さえ把握してしまえば難しくない。シンプルに書けるようにうまく設計されてる。

// テストデータクラス
struct MyTestParam {
    int v1;
    int v2;
    int ok_v;

    MyTestParam(
        const int v1,
        const int v2,
        const int ok_v
    ) : v1(v1),
        v2(v2),
        ok_v(ok_v) {
    }
};

// TestWithParamにテストデータのクラスを渡して、継承する
class MyTest : public testing::TestWithParam<MyTestParam> {
};

// TEST_Pでテストを書く
// GetParamでテストデータクラスのインスタンスを得る
TEST_P(MyTest, test) {
    const auto p = GetParam();

    EXPECT_EQ(p.v1 + p.v2, p.ok_v);
}

// データをインスタンス化して、テストを作る
INSTANTIATE_TEST_SUITE_P(OkTest, MyTest,
    testing::Values(
        MyTestParam(1, 2, 3),
        MyTestParam(2, 3, 5)
    )
);

実行結果。

Note: Google Test filter = */MyTest.*
[==========] Running 2 tests from 1 test suite.
[----------] Global test environment set-up.
[----------] 2 tests from OkTest/MyTest
[ RUN      ] OkTest/MyTest.test/0
[       OK ] OkTest/MyTest.test/0 (0 ms)
[ RUN      ] OkTest/MyTest.test/1
[       OK ] OkTest/MyTest.test/1 (0 ms)
[----------] 2 tests from OkTest/MyTest (2 ms total)

[----------] Global test environment tear-down
[==========] 2 tests from 1 test suite ran. (3 ms total)
[  PASSED  ] 2 tests.

OkTest/MyTest.test/0といった感じで出力される。この文字列でフィルタ出来るので、--gtest_filter=*/MyTest.*/0で、1つ目のデータだけで実行できる。

テストデータに名前を付ける (出力する)

/0/1の部分の文字列を置き換える。仕組み上任意の文字列...とまではできないのだけど、ある程度読みやすくなる。

簡単なのがテストデータに対してoperator<<を用意してあげる方法。合わせて、INSTANTIATE_TEST_SUITE_Pの最後に、testing::PrintToStringParamName()を渡す。

struct MyTestParam {
    std::string desc; // 追加
    int v1;
    int v2;
    int ok_v;

    MyTestParam(
        const std::string & desc, // こっちも更新
        const int v1,
        const int v2,
        const int ok_v
    ) : desc(desc),
        v1(v1),
        v2(v2),
        ok_v(ok_v) {
    }
};

// operator<<を作って、単純にdescを返す
std::ostream & operator<<(std::ostream & stream, const MyTestParam & p) {
    return stream << p.desc;
}

class MyTest : public testing::TestWithParam<MyTestParam> {
};

TEST_P(MyTest, test) {
    const auto p = GetParam();

    EXPECT_EQ(p.v1 + p.v2, p.ok_v);
}

INSTANTIATE_TEST_SUITE_P(OkTest, MyTest,
    testing::Values(
        MyTestParam(
            "1p2eq3", // テスト名をつける
            1, 2, 3
        )),
    testing::PrintToStringParamName() // 追加
);

実行結果。/0だったところに指定したテスト名が出力されている。

Note: Google Test filter = */MyTest.*
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from OkTest/MyTest
[ RUN      ] OkTest/MyTest.test/1p2eq3
[       OK ] OkTest/MyTest.test/1p2eq3 (0 ms)
[----------] 1 test from OkTest/MyTest (1 ms total)

[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (2 ms total)
[  PASSED  ] 1 test.

テストデータだけだと何を狙ったデータなのかの情報がロストする。コメントでもいいけど、なんとなく把握できるテスト名を適当につけとくと、コード的にもテスト結果的にも内容を把握する手助けになる。