Google Mock を導入してみた。
経緯
ようやく余裕ができてきたのか家でときどきコードを書いている。
最近色んな書き方を覚えてきたので、せっかくだし書き方を試してみようと思って、C++ とQtを使ってこんなコードを書いた。
下手くそな英語は放っておいて欲しいが、このクラスはコンストラクタから与えられた音素片のレポジトリと、波形のレポジトリと、音声の分析器を使って、特定条件の音声1フレーム分の分析結果を返すレポジトリクラスである(今思ったが Repository じゃなくて Service だろこれ、後で直そう)。コンストラクタから必要なレポジトリと分析アルゴリズムの実装を注入できるようにしてある。以前作っていたコードはテストの書けない形の実装が多く、二年程度でコードが腐ってしまったためテストの書けるコードにしておきたかった。
ともあれこれでモック実装を注入すれば簡単にテストができる、とQtのテスト用モッキングライブラリを探したもののどうも無さそう。テスト用の実装をいちいち書くのは馬鹿らしいので C++ 用のモッキングライブラリ Google Mock を導入してみた。
インストール
今回は Windows 7 に MinGW 4.8 の環境でインストールを行った。MinGW の bin にパスを通しておく必要がある。
- 以下のアドレスから Google Mock をダウンロードして解凍、適当な位置に配置する。
- Google Mock のルートディレクトリを GMOCK_DIR, ${GMOCK_DIR}/gtest をGTEST_DIR として以下のコマンドを実行する。
g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} -pthread -c ${GTEST_DIR}/src/gtest-all.cc g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} -isystem ${GMOCK_DIR}/include -I${GMOCK_DIR} -pthread -c ${GMOCK_DIR}/src/gmock-all.cc ar -rv libgmock.a gtest-all.o gmock-all.o
- 好み次第だが以下の環境変数に、GMOCK_DIR, GTEST_DIR のパスを通すと便利感はある。
- C_INCLUDE_PATH
- C_PLUS_INCLUDE_PATH
- LIBRARY_PATH
- Qt であれば
LIBS += -lgmock
をつけて MinGW のオプションを追加すれば良い。
使い方
インストールはこれで完了。あとはテストコードの main 関数に Google Mock を使用する際の初期化を書く。これでテスト内で Google Mock が使用できる。
#include "gmock/gmock.h" #include "gtest/gtest.h" #include "AutoTest.h" int main(int argc, char *argv[]) { ::testing::GTEST_FLAG(throw_on_failure) = true; ::testing::InitGoogleMock(&argc, argv); return QAutoTest::run(argc, argv); }
最後にモックを作るのだが、モック化したクラスは結局自前で定義する必要はある。自動化するスクリプトもあるのだが今回は使用していないので以下のようにモッククラスを定義した。
class MockPhonemeRepository : public ResourceRepository<PhonemeKey, Phoneme> { public: MOCK_CONST_METHOD1_T(find, const QSharedPointer<Phoneme>(const PhonemeKey &key)); MOCK_CONST_METHOD1_T(contains, bool(const PhonemeKey &key)); MOCK_METHOD2_T(add, bool(const PhonemeKey &key, QSharedPointer<Phoneme> value)); MOCK_METHOD1_T(remove, void(const PhonemeKey &key)); };
MOCK_METHOD*
で仮想メンバ関数の挙動を制御できるようにしてくれる。テンプレートのついた引数がある場合は、MOCK_METHOD*_T
を使い、const メンバ関数であればMOCK_CONST_METHOD*
を使うなど細かい違いはあるが概ねこんな感じでモック化する関数を定義しておくとメンバ関数がモック化される。 * は引数の数で変わる、10個まで行ける。
実際にモック化した関数の振る舞いは、以下のように指定する。
EXPECT_CALL(phonemeRepository, find(key)) .Times(1) .WillOnce(Return(phoneme));
find メソッドの呼び出しが1回、指定された key で行われる。その際一度だけ phoneme を返しなさい。くらいの指定で非常に便利に使える。この辺の使い方は良いドキュメントがあるので下記 URL を参考にするとよい。自前でテスト用のクラス実装するよりははるかに楽で、使い勝手もそこそこ良いように思う。