概要
前回、自分の仕事で使うCのためのGoogleMock環境まで作ったので、もう少し勉強してみることにしました。
「Google C++ Mocking Framework チートシート」とかあるにはあるんですがいまいちわかりづらくてさっと見ただけだと頭に入ってこないので、読みながらまとめてチートシート的なものを自分用に作ることにしました。
順次更新していきます。
マクロとかのまとめ
TEST()
, TEST_F()
, TEST_P()
TEST(TestSuiteName, TestName) {
... test body ...
}
- テストケースの大項目的な名前と小項目的な名前を入れます。
- アンダースコアを使うのは良くないらしいです。なんか使えますが。
- 複数のテストがある場合は大項目名と小項目名の組み合わせが重複しないようにしないとビルドエラーになります。
-
TEST_F
では第一引数はテストフィクスチャとして定義したクラス名にします。 -
TEST_P
は上級テクニックで、1つのテストをパラメーターを変えながら何度も実行することができます。
ASSERT_EQ
, EXPECT_EQ
- 第一引数が期待する値、第二引数が実際の値。関数を実行したときの戻り値のチェックなどに使えます。
-
EXPECT_NE
やEXPECT_TRUE
などの親戚もいます - 状況が期待と異なったときにすぐにテストが終了させられるのが
ASSERT_*
、続行するのがEXPECT_*
です。
MOCK_METHOD*
, MOCK_CONST_METHOD*
- mockのメソッドの定義に使います。
*
の部分は引数の数です。- 引数の数は10を超えると死んだりします。やたら引数が多い関数がある残念なプロジェクトでは対策が必要です。
EXPECT_CALL
- Mockのメソッドが呼び出されることを宣言します。第一引数がMock化されたクラスのインスタンス(ポインターではなく実体)、第二引数がメソッド名
- メソッド名は引数をセットで指定します。引数がマッチしない呼び出しをされた場合は呼び出されなかったことになります。
- 後述の
::testing::_
を使うことで引数のチェックを省けます
- 後述のNiceMockではない場合、EXPECTされていないMockメソッド呼び出しは警告されます。(テストは成功したりします)。EXPECTした呼び出しがなかった場合はテストは失敗になります。
::testing::_
- 正確な定義としてはMatcherのwildcardであり、その名のとおり任意の引数を許します
- Matcherは上記のページにいろいろあるので見ておくと何か使えるものがあるかもしれません
- 論理演算とかもあるようです。
Times
-
EXPECT_CALL
のメンバーのような形で使用して、呼び出し回数を指定できます。cardinality(基数)というそうです。 -
::testing::AtLeast(n)
(nは0以上の整数)で回数チェックを緩くできます - 指定回数まで呼び出されてないとテストは失敗になります
-
Times(0)
にすることでそのメソッドが呼び出されないことを宣言できます -
Times(1)
は省略できます。
WillOnce
, WillRepeatedly
-
EXPECT_CALL
のメンバーのような形で使用して、呼び出されたときのactionを定義できます- actionは主に戻り値の指定になると思います。戻り値がないメソッドではそれはできません。
-
Will**
はTimes
のあとに書く必要があります。 -
WillOnce
は複数使用でき、その都度の戻り値をactionを変えることができます。
using ::testing::Return;...
EXPECT_CALL(turtle, GetX())
.Times(5)
.WillOnce(Return(100))
.WillOnce(Return(150))
.WillRepeatedly(Return(200));
::testing::Return
- 上述の
Will**
のactionとして値を戻す動作をします - actionも上記のページのようにいくつか種類があるようです。使いどころがいまいちわからないですが……
RetiresOnSaturation
-
EXPECT_CALL
のメンバーのような形で使用して、そのEXPECT_CALLの役割が終わったらそれ以上EXPECT_CALLを見ないように指定できます。
Google Mock の Expectation がデフォルトでは,「sticky」であることを表しています
- stickyが具体的にどういうものかはいろいろテストを作って肌で学ぶしかないかと思います
::testing::Invoke
- 下記のように関数をactionとして定義できます。mockではなくfake的な使い方になりますね。
double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); }
...
EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));
- actionにInvokeで登録する関数は、Mockのメソッドと同じ関数の形(もしくはvoid*など互換性のある形)である必要があります。
- 使用しない引数はUnusedにすることもできます。上記の例でも使ってます。
-
InvokeWithoutArgs
などもあるようです。これは引数を受け取らない代わりに一切の引数を無視できるようです。 - Actionを定義するなどもありますが……
NiceMock<>
- これで宣言されたMockは、そのMockのメソッドが
EXPECT_CALL
で設定される前に呼ばれてもAssertなどを出しません。- 「まずは EXPECT_CALL を明示してモックの挙動を指定しましょう」
- 「非常に慎重に利用するべきです」「バグが警告されず見過ごされる可能性があります」
- どうでもいいクラスに対してのみ使うようにするのを私もおすすめします
- 親戚にStrictMockもいます。
文法的なもの
テストフィクスチャ
-
::testing::Test
から派生したクラスを定義してフィクスチャ(妥当な日本語がないが、定番とか基本的な概念といったニュアンスだと思います)を作れます。 - 具体的には、フィクスチャで作成したインスタンスをテストケース間で共有したりとかできます。、
RUN_ALL_TESTS
とmain関数
http://opencv.jp/googletestdocs/primer.html#primer-invoking-the-tests
http://opencv.jp/googlemockdocs/fordummies.html#fordummies-using-mocks-in-tests
- フィクスチャやモックを使用する場合はそれらの初期化のためにmain()が必要なようです。
- main()を用意した場合はmain()のreturnで
RUN_ALL_TESTS()
を実行するようにすると良いようです。-
RUN_ALL_TESTS
ですべてのテストを実行します。このとき、Setup()とTearDown()が各テストごとに実行されることに注意が必要です。
-
::testing::InitGoogleMock
- Mockを使用する場合の初期化をするようです。詳しくはよくわからない……おまじない状態です。
複数のException(EXPECT_CALL
)
- 複数のmatcher(match条件)に対するEXPECT_CALLをあらかじめ書いておくことができます。
EXPECT_CALL(turtle, Forward(_)); // #1
EXPECT_CALL(turtle, Forward(10)) // #2
.Times(2);
- ただし複数のExceptionは独特の動作をするのでテストに苦労することがあります:
- Matchの判定は最後に定義されたものから順にするようです
- 同じmatcherに対して複数のEXPECT_CALLを書くと最後に宣言したもので上書きされたりします
- 同じMatch条件で複数の動作をさせたい場合はWillOnceを複数つけたり、
::testing::Invoke
を使います
InSequence
- メソッドの呼び出し順をチェックできます
- あまり使わないので詳説できないです。呼び出し順をテストしなければいけない状況がすでに終わってる……でもうまく使えばテストコードが見やすくなるんでしょうか?
- ここら辺になるともう中級テクニックな感じですね
- 別の例ではEXPECT_CALLにくっつけてシーケンスを定義する方法も書いてあります。
- 順序に関連してAfterというのあります。
ON_CALL
とデフォルトのaction
-
ON_CALL
でメソッドのデフォルトのactionを指定するようです - Times未指定のWillRepeatedlyでも同じようなことができますが、ON_CALLを使うのが本来の手順なのかもしれません
-
DefaultValue<T>::Set(value)
というのもあるようです。
よくわからなかったやつ
MOCK_METHOD_1_WITH_CALLTYPE
-
::testing::Mock::VerifyAndClearExpectations
+ その仲間- destructionを検証できる?