Google C++ Mocking Framework のよくある質問
質問は, googlemock ディスカッショングループに送ってください.また,コンパイルエラーで困っているならば,まずは Google Mock Doctor を試してください.
モック化されるメソッドは仮想関数である必要があります.例外は, 高効率な依存性の注入 のテクニックを使う場合のみです.
Google Mock バージョン 1.4.0 がリリースされた後,我々は,役立つメッセージを効率的に生成することができる matcher をより簡単に書くためのアイディアを思いつきました.このアイディアを試してみて,気に入ったので,それを実装することにしたのです.
残念なことに,MatcherInterface を実装したり,MakePolymorphicMatcher() を使用したりして,ユーザが独自の matcher を定義している場合,その定義はコンパイルできないことになります.MATCHER* 形式のマクロを使って定義された matcher には影響がありません.
もし,あなたの matcher が影響を受けたのなら,お手数をおかけします.我々は,すぐにでもこの変更を行うことが,ユーザ全員の長期的利益になると信じています.幸いなことに,既存の matcher を,新しい API に移植するのは通常は難しいことではありません.以下に,必要な作業を示します:
あなたの matcher が,次のように書かれていたとします:
// 最新の Google Mock では動作しない,
// 古い matcher 定義
using ::testing::MatcherInterface;
...
class MyWonderfulMatcher : public MatcherInterface<MyType> {
public:
...
virtual bool Matches(MyType value) const {
// value がマッチすると true を返します.
return value.GetFoo() > 5;
}
...
};
これを,次のように書き換える必要があります:
// 最新の Google Mock で動作する,新しい matcher 定義.
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
...
class MyWonderfulMatcher : public MatcherInterface<MyType> {
public:
...
virtual bool MatchAndExplain(MyType value,
MatchResultListener* listener) const {
// value がマッチすると true を返します.
return value.GetFoo() > 5;
}
...
};
(つまり,名前を Matches() から MatchAndExplain() に変更し,2番目の引数に MatchResultListener* を与えます.)
matcher のメッセージを改良するために,ExplainMatchResultTo() も利用しているならば:
// 最新の Google Mock では動作しない,
// 古い matcher 定義
using ::testing::MatcherInterface;
...
class MyWonderfulMatcher : public MatcherInterface<MyType> {
public:
...
virtual bool Matches(MyType value) const {
// value がマッチすると true を返します.
return value.GetFoo() > 5;
}
virtual void ExplainMatchResultTo(MyType value,
::std::ostream* os) const {
// なぜ value がマッチしたのか(または,しなかったのか)
// を表す,有用な情報を os に出力します.
*os << "the Foo property is " << value.GetFoo();
}
...
};
::std::ostream が利用されていた箇所で MatchResultListener の引数を利用して,ExplainMatchResultTo() のロジックを,MatchAndExplain() に移動する必要があります.
// 最新の Google Mock で動作する,新しい matcher 定義.
using ::testing::MatcherInterface;
using ::testing::MatchResultListener;
...
class MyWonderfulMatcher : public MatcherInterface<MyType> {
public:
...
virtual bool MatchAndExplain(MyType value,
MatchResultListener* listener) const {
// value がマッチすると true を返します.
*listener << "the Foo property is " << value.GetFoo();
return value.GetFoo() > 5;
}
...
};
MakePolymorphicMatcher() を用いて matcher を定義している場合:
// 最新の Google Mock では動作しない,
// 古い matcher 定義
using ::testing::MakePolymorphicMatcher;
...
class MyGreatMatcher {
public:
...
bool Matches(MyType value) const {
// value がマッチすると true を返します.
return value.GetBar() < 42;
}
...
};
... MakePolymorphicMatcher(MyGreatMatcher()) ...
Matches() メソッドを MatchAndExplain() にリネームして,MatchResultListener* 引数を追加してください(MatcherInterface を実装して定義した matcher の場合と,やることは同じです):
// 最新の Google Mock で動作する,新しい matcher 定義.
using ::testing::MakePolymorphicMatcher;
using ::testing::MatchResultListener;
...
class MyGreatMatcher {
public:
...
bool MatchAndExplain(MyType value,
MatchResultListener* listener) const {
// value がマッチする場合に true を返します.
return value.GetBar() < 42;
}
...
};
... MakePolymorphicMatcher(MyGreatMatcher()) ...
ポリモーフィック matcher で,失敗メッセージを分かりやすくするために ExplainMatchResultTo() を使用している場合:
// 最新の Google Mock では動作しない,
// 古い matcher 定義
using ::testing::MakePolymorphicMatcher;
...
class MyGreatMatcher {
public:
...
bool Matches(MyType value) const {
// value がマッチすると true を返します.
return value.GetBar() < 42;
}
...
};
void ExplainMatchResultTo(const MyGreatMatcher& matcher,
MyType value,
::std::ostream* os) {
// なぜ value がマッチしたのか(または,しなかったのか)
// を表す,有用な情報を os に出力します.
*os << "the Bar property is " << value.GetBar();
}
... MakePolymorphicMatcher(MyGreatMatcher()) ...
ExplainMatchResultTo() 内部のロジックを MatchAndExplain() に移動する必要があります:
// 最新の Google Mock で動作する新しい matcher 定義.
using ::testing::MakePolymorphicMatcher;
using ::testing::MatchResultListener;
...
class MyGreatMatcher {
public:
...
bool MatchAndExplain(MyType value,
MatchResultListener* listener) const {
// value がマッチすると true を返します.
*listener << "the Bar property is " << value.GetBar();
return value.GetBar() < 42;
}
...
};
... MakePolymorphicMatcher(MyGreatMatcher()) ...
詳細は,クックブックのこれら 2つの レシピ を参照してください.また,助けが必要ならいつでも googlemock@googlegroups.com に質問を投げてください.
Google Test があれば Google Mock を簡単に利用できます.しかし,任意の testing framework で動作するように設定するのは簡単です.やり方は, ここ を参照してください.
gcc が出すコンパイルエラーに混乱した場合は,まず,Google Mock Doctor ツールを試してみてください.これは,stdin を介して gcc エラーメッセージをスキャンし,あなたのコードが抱える問題(我々は,これらを疾病を呼びます)を診断結果に吐き出します.
「インストール」するには,次のコマンドを実行してください:
alias gmd='<path to googlemock>/scripts/gmock_doctor.py'
利用するには,次のようにします:
<your-favorite-build-command> <your-test> 2>&1 | gmd
以下に例を示します:
make my_test 2>&1 | gmd
あるいは,gmd を実行して,それに gcc のエラーメッセージをコピー&ペーストすることもできます.
Google Mock では,可変個引数の関数(つまり,省略記号(...)引数をとる関数)を直接モック化することはできません.
一般的に,この問題は,可変個引数メソッドに何個の引数が渡されたのか,そして引数の型は何なのか,をモックオブジェクトが知る方法がないということです.基底クラスを書いた人だけが,このプロトコルを知っており,我々は彼の頭の中を覗くことはできません.
したがって,このような関数をモック化するには,引数の個数と型を知る方法をモックオブジェクトに教えなければいけません.方法の1つは,この関数のオーバーロードバージョンを用意することです.
省略記号引数は,C から引き継がれたもので,本当は C++ の機能ではありません.これは,安全に利用できるものではなく,コンストラクタやデストラクタを持つ引数と一緒には動作しません.したがって,C++ では,できるだけこれらを利用しないことをお勧めします.
Microsoft Visual C++ 2005 SP1 を用いて,以下をコンパイルするとします:
class Foo {
...
virtual void Bar(const int i) = 0;
};
class MockFoo : public Foo {
...
MOCK_METHOD1(Bar, void(const int i));
};
すると,以下のような警告が出るでしょう:
warning C4301: 'MockFoo::Bar': overriding virtual function only differs from 'Foo::Bar' by const/volatile qualifier
これは MSVC のバグです.例えば,gcc では同じコードが問題なくコンパイルできます.また, Visual C++ 2008 SP1 を利用している場合は,次のような警告が出ます:
warning C4373: 'MockFoo::Bar': virtual function overrides 'Foo::Bar', previous versions of the compiler did not override when parameters only differed by const/volatile qualifiers
C++ では,const なパラメータを使って関数を宣言した場合,その const 修飾子は無視されます.したがって,上述の Foo 基底クラスは,以下と等価になります:
class Foo {
...
virtual void Bar(int i) = 0; // int または const int ?どちらでも同じです.
};
実際,int パラメータを使って Bar() を宣言して,const int パラメータを使って定義することもできます.コンパイラは,これらをうまく合致させます.
メソッドの宣言でパラメータを const にする意味がないので,Foo と MockFoo からそれを削除することをお勧めします.これで,VC のバグを回避できます.
ここでは,トップレベルの const 修飾子について論じていることに注意してください.関数パラメータがポインタや参照で渡される場合,そのポインタや参照の指し示す先を const として宣言することは大きな意味があります.次の2つの宣言は,等価ではありません:
void Bar(int* p); // p または *p のどちらも const ではありません.
void Bar(const int* p); // p は const ではありませんが,*p は const です.
我々は,/clr コンパイラフラグを使うと,Visual C++ でモッククラスをコンパイルする際に 5〜6 倍のメモリを消費することに気づきました.ネイティブな C++ モックをコンパイルする場合は,/clr を使わないようにするのが良いでしょう.
—gmock_verbose=info を利用してテストを実行すると良いでしょう.このフラグを使うと,Google Mock が受けた各モック関数の呼び出しのトレースを出力します.このトレースを調べることで,あなたが設定した Expectation が満足されない理由を見抜くことができるようになります.
EXPECT_CALL(foo, Bar(_))
.Times(0);
Google Mock が失敗を検出すると,ユーザがデバッグしやすいように,関連情報(モック関数の引数,関連する Expectation の状態,など)が出力されます.別の失敗を検出した場合も,Google Mock は同様のことを行い,やはり関連する Expectation の状態が出力されます.
これら2つの失敗の間で,Expectation の状態が変化しないこともあるので,この状態の同じ説明を2度見ることがあります.しかし,その時々で異なるポイントを参照しているので,これは冗長ではありません.これらが等しい,ということ自体が興味深い情報です.
モック化しようとしているクラス(できれば,純粋なインタフェース)は,仮想デストラクタを持っていますか?
基底クラスからの派生を行う場合,必ずそのデストラクタを仮想関数にしてください.そうしなければ,困ったことになります.次のコードを見てください:
class Base {
public:
// 仮想関数であるべきですが,これはそうではありません.
~Base() { ... }
...
};
class Derived : public Base {
public:
...
private:
std::string value_;
};
...
Base* p = new Derived;
...
delete p; // なんと! ~Base() が呼び出されても,~Derived() は呼び出されません.
// - value_ がリークします.
~Base() を仮想関数にすると,p を削除した際に ~Derived() が正しく呼び出されるので,ヒープチェッカーも満足します.
これについて文句を言う場合,よく次のようなコードが持ち出されます:
// foo.Bar() は2回呼び出され,最初は1を,2度目は2を返すはずです.
// しかし,Expectation はその逆の順番で書く必要があります.
// これはヒドイ!!!
EXPECT_CALL(foo, Bar())
.WillOnce(Return(2))
.RetiresOnSaturation();
EXPECT_CALL(foo, Bar())
.WillOnce(Return(1))
.RetiresOnSaturation();
この問題は,テストの意図を表現するための最適な方法をとらないことにあります.
デフォルトでは,Expectation は,ある特定の順番でマッチしなければならないというわけではありません.ある順序でマッチしてほしいならば,それを明示する必要があります.テストは過剰に指定されやすいので,簡単にはそうできないようにしたい,というのが Google Mock の(そして,jMock の)基本哲学です.
テストの仕様を記述する,より良い方法は2つあります.どちらの場合も,Expectation を順序どおりに書くことができます:
// foo.Bar() は2回呼び出され,最初は1を,
// 2度目は2を返すはずです.シーケンスを利用すると,
// 自然な順番で Expectation を書くことができます.
{
InSequence s;
EXPECT_CALL(foo, Bar())
.WillOnce(Return(1))
.RetiresOnSaturation();
EXPECT_CALL(foo, Bar())
.WillOnce(Return(2))
.RetiresOnSaturation();
}
または,一連の Action を同じ Expectation に書きます:
// foo.Bar() は2回呼び出され,最初は1を,
// 2度目は2を返すはずです.
EXPECT_CALL(foo, Bar())
.WillOnce(Return(1))
.WillOnce(Return(2))
.RetiresOnSaturation();
最初の質問に戻ります:Google Mock はなぜ,Expectation(および ON_CALL)を後ろから前に探索するのでしょうか?このおかげで,ユーザは,まず共通のケースに対するモックの挙動を定義して(例えば,モックのコンストラクタやテストフィクスチャの set-up フェーズ),それから,より具体的なルールを用いてそれをカスタマイズできるからです.もし,Google Mock が前から後ろに探索を行うと,この非常に便利なパターンは利用できなくなるでしょう.
綺麗であることと安全であることの,どちらかを選択する場合,我々は後者を好みます.よって,警告を出したほうが良い,というのがこの質問に対する答えです.
テスト毎に変更さることが滅多にないデフォルトの挙動として,モックオブジェクトのコンストラクタや SetUp() 内に ON_CALL を書く人をよく見かけます.そして,テスト本体部分に Expectation を設定しますが,それは各テストごとに異なります.テストの set-up 部分に ON_CALL があるからといって,その呼び出しが期待されているという意味にはなりません.EXPECT_CALL がない場合にそのメソッドが呼ばれると,場合によってはエラーになります.そのような呼び出しを,ユーザに知らせることなく黙って通過させると,気づかないうちにバグが入り込む可能性があります.
しかし,もし,その呼び出しが問題ないことが分かっていれば,次のように書くことができます.
EXPECT_CALL(foo, Bar(_))
.WillRepeatedly(...);
これは,以下の代わりになります.
ON_CALL(foo, Bar(_))
.WillByDefault(...);
これは,その呼び出しを期待しており警告を出力するべきではない,ということを Google Mock に伝えます.
—gmock_verbose フラグを使って,出力メッセージの詳細レベルを制御できます.デバッグ時に,出力に邪魔なものが多すぎると思った場合は,このレベルを下げてください.
Google Mock が直接サポートしていない Action を実行する必要がある場合, MakeAction() や MakePolymorphicAction() を利用して自分で Action を定義したり,スタブ関数を書いて Invoke() でそれを呼び出すことができることを思い出してください.
なぜか?それが綺麗だと思っているからです.:-)
どの構文が自然に見えるかは,ある程度は主観の問題です.Google Mock の構文は,いくつかの実際的な利点を考慮して選択されました.
引数に map をとる関数をモック化してみましょう:
virtual int GetSize(const map<int, std::string>& m);
提案にあった構文を使うと,これは次のようになります:
MOCK_METHOD1(GetSize, int, const map<int, std::string>& m);
さて,これはどうなるでしょう?コンパイラは const map<int, std::string>& m を1つではなく2つの引数と考えるので,コンパイルエラーが起こります.これを動作させるには,面倒ですが,与えられた map を typedef する必要があります.Google Mock の構文では,関数の引数が丸括弧内に保護されているので,このような問題を避けることができます.
// これはコンパイルできます.
MOCK_METHOD1(GetSize, int(const map<int, std::string>& m));
もし戻り値の型にカンマがあれば,typedef が必要になりますが,そのようなケースは非常にまれです.
他にも,次のような利点があります:
できますが,いくつかの変更が必要です.
一般的にいって,static 関数をモック化したくなったとすると,それはモジュール同士がが強く結びつきすぎている(そして,柔軟性や再利用性,テスト可能性などが低下している)ことを示すサインです.おそらく,より小さなインタフェースを定義し,そのインタフェースを介してメソッドを呼び出すほうが良いでしょう.そのようなメソッドは,簡単にモック化できます.それは,ちょっとした仕事になりますが,すぐに元が取れます.
Google Testing Blog のこの ポスト が,それを見事に述べています.ぜひ見てください.
これが質問ではないことは分かっていますが,ともかく回答を書きます.:-)
Google Mock を使うと,C++ で簡単にモックを作ることができます.そして,あらゆる場所でそれを使おうとするかもしれません.非常に上手く使えることもあれば,えー,まったく苦痛でしかないこともあります.後者の場合では,何が問題なのでしょうか?
モックを利用せずにテストを書く場合,コードを実行して,それが正しい値を返すことや,システムが期待した状態にあることをアサートします.これは,「状態に基づくテスト(state-based testing)」と呼ばれることがあります.
モックが役立つのは,「インタラクションに基づく(interaction-based)」テストと呼ばれるものに対してです.このテストでは,すべて終わった後にシステムの状態をチェックする代わりに,モックオブジェクトが,自分自身が正しく実行されたかを検証し,エラーが起これば即座にレポートします.これにより,ユーザはエラーが発生したときの正確なコンテキストを把握することができます.これは,状態に基づくテストに比べて,より便利で経済的な場合が多いです.
もし,あなたが状態に基づくテストを行っており,実際のオブジェクトをシミュレートするためだけにテストダブルを使っているならば,フェイクを使ったほうが良いでしょう.モックは,複雑な動作を行うことを重視していないので,このような場合にモックを利用しても苦痛なだけです.もし,このようなことで悩み,モックは駄目だと思ったなら,それは単に問題に合った正しい道具を使っていないだけです.あるいは,間違った問題を解こうとしているのかもしれません.:-)
いいえ,そんな必要はまったくありません.これは,単に参考のための情報です.
これは,Expectation を設定していないモック関数があり(Google Mock のルールでは,この関数呼び出しに関心が無いので,任意の回数呼び出すことができます),それが呼び出された,ということを意味しています.大丈夫,問題ありません.この関数を呼び出してはいけない,とは決めませんでしたから.
実際にはこの関数の呼び出しを許可していないつもりで,EXPECT_CALL(foo, Bar()).Times(0) を書き忘れていたのなら,どうでしょうか?それがユーザの誤りだと言える場合でも,Google Mock は,気を使って,注意表示を行います.
不要な呼び出しは1つも無いはずだと信じている場合でも,このメッセージを目にしたら,何が起こっているのかを調べてみるべきです.不要な呼び出しが起こった場合に悩まなくてもすむように,Google Mock はその関数名と引数を出力します.
どちらでも構いません.が,状況に応じて便利な方を選択したい,というわけですね.
あなたの Action が特定の関数型に対するものならば,通常は Invoke() を用いて定義するのが簡単でしょう.その Action が異なる型の関数内で利用される可能性がある場合(例えば,Return(value) を定義している場合)は,MakePolymorphicAction() が最も簡単です.また,関数(この内部で Action が利用されます)の型を正確に制御したい場合もありますが,その場合は,ActionInterface を実装する方法を採ります.例として,include/gmock/gmock-actions.h における Return() の実装を参照してください.
モックメソッドが呼ばれたときに,Google Mock がどの値を返すべきか分からないと,このエラーが起こります.SetArgPointee() を使うことで,副作用が何なのかは分かりますが,何の値を返すべきかは分かりません.DoAll() を利用して,SetArgPointee() に続けて Return() を呼ぶ必要があります.
詳細は,この レシピ を参照してください.
もし,疑問の答えがこの FAQ に載っていない場合でも,他にも利用できるリソースがあります:
issue tracker は,ごく少数の人間がたまに見ているだけなので,質問に答えてもらうために,ここに issue を作成するのは良い方法ではありません.
質問するときは,以下の情報をできるだけ多く提示することが役に立ちます(質問に関する情報が十分でなければ,手助けできません):