超入門編

サルでも分かる Google C++ Mocking Framework

(注意:分からないコンパイルエラーが出たら, Google Mock Doctor を試してください.)

Google C++ Mocking Framework とは何か?

プロトタイプやテストを書くとき,実際のオブジェクトにべったり依存するのは不可能だったり,賢明とはいえないことだったりします. モックオブジェクト は,実際のオブジェクトと同じインタフェースを実装したものです(そして同じように利用できます).ただし,実行時の使われ方や動作内容(どのメソッドが呼ばれるか,どんな順番で呼ばれるか,何回呼ばれるか,引数は何か,何を返すか,など)を指定することができます.

注意 :フェイクオブジェクト と モックオブジェクト は,混同されがちです.実際は,テスト駆動開発(TDD)コミュニティにおける,フェイクオブジェクトとモックオブジェクトの意味はまったく異なります.

  • フェイク オブジェクトは動作する実装ですが,通常は,(おそらく処理を軽くするための)省略を行っているので,これは製品には適したものではありません.例えば,メモリ上のファイルシステムは,一種のフェイクといえます.
  • モック は,Expectation を利用して事前にプログラムされたオブジェクトです.この Expectation が,期待される関数呼び出しの仕様を決定します.

この説明が抽象的過ぎると感じても心配はありません.最も重要なことは,モックの場合,モックとモックを利用するコードとのインタラクションが可能である,ということです.モックを使い始めれば,フェイクとモックの違いがさらに明確になるでしょう.

Google C++ Mocking Framework(略して Google Mock)は,モッククラスを作成して使用するためのライブラリ(カッコいいので,「フレームワーク」と言うこともあります)です.つまり,Java における jMockEasyMock の C++ 版です.

Google Mock を使うための3つの基本ステップは,次の様になります:

  1. 簡単なマクロを使ってモック化したいインタフェースを記述します.これが,モッククラスの実装に展開されます.
  2. モックオブジェクトを作成し,直感的な構文を利用して Expectation と 動作を規定します.
  3. モックオブジェクトを利用するコードを実行します.Google Mock は,Expectation 違反が起こると即座にそれをキャッチします.

どうして Google Mock を使うのか?

モックオブジェクトをうまく使えば,不要な依存関係がなくなり,テストを高速で確実なものにできますが,いかんせん C++ のモックを手動で書くのは大変です:

  • モックを実装する必要があるとします.しかし,この仕事は退屈でミスが起こりやすいものです.皆が敬遠するのも無理ありません.
  • 手で書かれたモックの品質は,なんというか,予測しにくいものです.中には洗練されたものもあるかもしれませんが,あわてて作られたその場しのぎのものもあるかもしれません.
  • そして,あるモックを利用して得た知識は,次に受け継がれません.

これとは対照的に,Java と Python のプログラマには,モックの作成を自動化してくれる良いモックフレームワークがあります.よって,モック化は効率的なテクニックで,これらのコミュニティでは広く受け入れられた習慣となっています.適切な道具を得ることが非常に重要なのです.

Google Mock は,C++ プログラマ達を助けるために作られました.これは jMockEasyMock に着想を得たものですが,C++ の仕様を考慮して設計されています.次のような問題で悩んでいるならば,これが役に立つでしょう:

  • 準最適化設計で行き詰まり,手遅れになる前にもっとプロトタイピングをしていれば,と後悔している.しかし,C++ でのプロトタイピングは決して「早く」ない.
  • 多くのライブラリに依存しすぎて,またはコストの高いリソース(例えば,データベース)を使用しているせいで,テストが遅い.
  • 使用しているリソースの信頼性が低い(例えば,ネットワーク)せいで,テストが不安定である.
  • プログラムが失敗(例えば,チェックサムエラー)をどのように処理するかのテストをしたいが,それを起こすのが難しい.
  • モジュール間のインタフェースが正しいことを確認する必要があるが,そのインタラクションを観測するのは大変なので,つい動作終了時の副作用結果を観測するだけ,という不恰好な方法で済ませてしまう.
  • 依存対象の「モック」を作成したいが,モックの実装はやりたくない.正直に言って,モックを手書きする作業はつまらない.

以下に,Google Mock のお勧め利用方法を示します:

  • デザインツールとして利用すれば,インタフェースデザインの実験を初期段階で何度も行うことができます.多く使い込むほどに良いデザインになります.
  • テストツールとして利用すれば,外部依存関係を切り離し,自分のモジュールと相手とのやり取りを徹底的に検証できます.

はじめ方

Google Mock を使うのは簡単です!C++ ソースファイル内で, “gtest/gtest.h” と “gmock/gmock.h” を #include するだけで準備は終わりです.

Mock Turtles の場合

さて,例を見ていきましょう. LOGO 風の描画用 API を使ったグラフィックプログラムを開発しているとします.これの動作が正しいかどうかを,どうやってテストすればよいのでしょうか?プログラムを実行して,その結果を検証用のスナップショット画像と比較することもできますが,そのようなテスト方法は,実行コストが高く不安定であることを認識してください(優れたアンチエイリアス機能を持つ新品のグラフィックカードにアップグレードしたら?それだけで,検証用の全ファイルを更新する必要ができてしまいます).全てのテストをこのように行うのは,辛すぎる作業かもしれませんが,幸いにも,あなたは依存性の注入について学び,その実現方法を知っています:つまり,アプリケーションが描画用 API を直接叩く代わりに API をインタフェース(これを,Turtle と呼びましょう)でラップして,このインタフェースに対するコードを書きます.

class Turtle {
  ...
  virtual ~Turtle() {}
  virtual void PenUp() = 0;
  virtual void PenDown() = 0;
  virtual void Forward(int distance) = 0;
  virtual void Turn(int degrees) = 0;
  virtual void GoTo(int x, int y) = 0;
  virtual int GetX() const = 0;
  virtual int GetY() const = 0;
};

(Turtle のデストラクタは, 必ず virtual であることを注意してください.これは,ここから派生する 全て のクラスでも同様です.そうしなければ,基底クラス型のポインタを介してオブジェクトを削除した場合に,派生クラスのデストラクタが呼ばれなくなり,メモリリークのような状態に陥るでしょう.)

PenUp() と PenDown() を利用して,turtle が移動の軌跡を残すか否かを制御できます.また,Forward() ,Turn() ,そして GoTo() を使って,それ自身の移動を制御できます.そして,GetX() と GetY() によって turtle の現在位置が分かります.

普段のプログラムでは,このインタフェースを本当に実装したものを利用するでしょう.テストでは,代わりにモック実装を利用します.これにより,どの描画プリミティブがプログラムから呼び出されたか,引数は何か,順番はどうか,といったチェックを簡単に行うことができます.このように書かれたテストは,非常にロバスト(マシンのアンチエイリアスが変化したせいで破綻したりはしません)で,解読とメンテナンスが容易(テストの目的は,画像ファイルではなくコードで表現されます),そして,テストの実施が各段に高速です.

モッククラスを書く

運が良ければ,だれか親切な人が,あなたが必要としているモックを既に書いているかもしれません.しかし,あなたが自分でモッククラスを書かなければいけないとしても,大丈夫,落ち着いてください.Gogle Mock にかかれば,この作業も楽しいゲーム(みたいなもの)に変わります.

どうやって定義するか

Turtle インタフェースを例に考えてみましょう.ステップを簡単に示すと以下のようになります:

  1. Turtle から MockTurtle クラスを派生させます.
  2. Trutle の仮想関数(テンプレートを利用して非仮想関数をモック化することも可能ですが,かなり複雑です)を調べて,引数の数を数えます.
  3. 派生クラスの public セクションに,MOCK_METHODn();(const メソッドをモック化する場合は, MOCK_CONST_METHODn();)を書きます.ここで n は,引数の数を表しますが,これを数え間違えると,コンパイルエラーで注意されます.
  4. ここが楽しい箇所です:関数のシグネチャを調べて,その関数名をマクロの1番目の引数に,残りを2番目の引数にします(念のため書いておくと,これが関数の型です).
  5. これを,モック化したい仮想関数全てに対して繰り返します.

この処理が終わると,次のようになっているでしょう:

#include "gmock/gmock.h"  // Google Mock はこのヘッダに.
class MockTurtle : public Turtle {
 public:
  ...
  MOCK_METHOD0(PenUp, void());
  MOCK_METHOD0(PenDown, void());
  MOCK_METHOD1(Forward, void(int distance));
  MOCK_METHOD1(Turn, void(int degrees));
  MOCK_METHOD2(GoTo, void(int x, int y));
  MOCK_CONST_METHOD0(GetX, int());
  MOCK_CONST_METHOD0(GetY, int());
};

これらのモックメソッドを別の場所に書く必要はありません.MOCK_METHOD* マクロが,定義を生成してくれます.簡単ですね!一度コツが分かれば,ソース管理システムがチェックインを処理するよりも早く,モッククラスを作れるようになります.

ヒント: これでも大変過ぎるというあなたのために,Google Mock の scripts/generator/ ディレクトリに gmock_gen.py という便利なツールが用意してあります( cppclean プロジェクトからの好意によります).このコマンドラインツールを使うには,Python 2.4 のインストールが必要です.このツールに,C++ ファイルと,そこで定義される抽象クラス名を与えると,モッククラスの定義が出力されます.C++ は複雑な言語なので,このスクリプトは常に完全に動作するわけではありませんが,動けば非常に便利なものです.詳細は, ユーザドキュメント を参照してください.

どこに書くか

モッククラスを定義する場合,その定義をどこに書くかを決める必要があります.これを *_test.cc に書く人達もいます.同じ人や同じチームが,モック化されているインタフェース( Foo とします)も所有しているならば,これは良い方法です.しかし,そうでない場合,Foo の所有者がそれを変更してしまうと,あなたのテストが破綻する可能性があります(Foo のメンテナが, Foo を利用しているあらゆるテストを修正してくれる,とは期待できませんよね?).

そこで,大まかなルールを次のように決めます:他人の所有する Foo をモック化する必要がある場合,Foo パッケージの中でモッククラスを定義して(製品版コードとテストユーティリティを確実に分離できるように,テスト用サブパッケージ内で定義するとさらに良いでしょう),mock_foo.h 内にこれを含めます.そうすると,各テストから mock_foo.h を参照できます.Foo が変更されても,1つの MockFoo と,変更されたメソッドに依存するテストだけを修正すれば良いことになります.

別の方法:Foo の上に乗せる薄いレイヤー FooAdaptor を導入して,この新しいインタフェースに対するコードを書きます.この FooAdaptor の所有者は自分なので,Foo の変更をより簡単に吸収することができます.導入コストは少し高いですが,特定の目的に対して Foo よりも適切な FooAdaptor を選択することができるので,アダプタインタフェースを慎重に選択するとコーディングがより簡単になり,可読性も向上します(長期的に見れば利点のほうが多いでしょう).

テストでモックを使う

モッククラスが用意できたら,使うのは簡単です.典型的な手順は,次のようになります:

  1. モックを制限無く利用できるように,Google Mock の名前をテスト用の名前空間からインポートします(各ファイルで1度だけ必要です.名前空間は優れたアイディアであることをお忘れなく).
  2. モックオブジェクトを作成します.
  3. その Expectation を設定します(メソッドが何回呼ばれるのか?引数は何か?何を行うのか?など).
  4. モックを利用したコードを実行します.Google Test のアサーションを利用して結果のチェックを行っても良いでしょう.モックメソッドが期待よりも多く呼び出される,または引数が誤っている,などの場合は,即座にエラーが起こります.
  5. モックがデストラクトされると,その全ての Expectation が満足されたかどうかを Google Mock が自動的に検証します.

以下に例を示します:

#include "path/to/mock-turtle.h"
#include "gmock/gmock.h"
#include "gtest/gtest.h"
using ::testing::AtLeast;                     // #1

TEST(PainterTest, CanDrawSomething) {
  MockTurtle turtle;                          // #2
  EXPECT_CALL(turtle, PenDown())              // #3
      .Times(AtLeast(1));

  Painter painter(&turtle);                   // #4

  EXPECT_TRUE(painter.DrawCircle(0, 0, 10));
}                                             // #5

int main(int argc, char** argv) {
  // 以下の行は,テスト開始前に Google Mock (と Google Test)
  // を初期化するために必ず実行する必要があります.
  ::testing::InitGoogleMock(&argc, argv);
  return RUN_ALL_TESTS();
}

想像通りかもしれませんが,これは PenDown() が少なくとも1度は呼ばれることをチェックするテストです.もし描画オブジェクトがこのメソッドを呼び出さなければ,テストは失敗し,次のようなメッセージを出力します

path/to/my_test.cc:119: Failure
Actual function call count doesn't match this expectation:
Actually: never called;
Expected: called at least once.

ヒント1: Emacs のバッファからテストを実行している場合,エラーメッセージに表示された行番号上で <Enter> を押して,失敗した Expectation の場所に飛ぶことができます.

ヒント2: もしモックオブジェクトが削除されなければ,最終的な検証作業が行われません.モックをヒープ上に確保する場合,テスト内でヒープリークチェッカーを使うのは良いアイディアです.

重要な注意事項: Google Mock では,モック関数が呼び出される前に Expectation が設定されている必要があります.そうでない場合の挙動は未定義となります.特に,EXPECT_CALL() とモック関数の呼び出しをごちゃ混ぜにするのは,決してやってはいけません.

この EXPECT_CALL() は,既に起こった呼び出しではなく,将来的に起こる呼び出しに対して期待する動作,と見るべきものです.Google Mock は,何故このような動作になっているのでしょうか?Expectation を予め決めておくことで,Google Mock は違反が起こったら即座に,つまり(スタックトレース,などの)コンテキストがまだ有効な状態で,それを報告することができます.これにより,デバッグが非常にやりやすくなります.

正直なところ,このテストは不自然で,たいした仕事をしません.Google Mock 無しでも同様のことが簡単にできます.しかし,この後すぐに分かるように,Google Mock を使うことで,モックを利用してさらに多くのことができます.

任意の Testing Framework で Google Mock を利用する

Testing Framework として Google Test 以外のもの(例えば, CppUnito や CxxTest)を使いたい場合,上述のセクションの main() 関数を次のように変更するだけです:

int main(int argc, char** argv) {
  // 以下の行で,Google Mock が失敗した場合に,
  // Testing Framework にテスト失敗と解釈される例外を投げるようにします.
  ::testing::GTEST_FLAG(throw_on_failure) = true;
  ::testing::InitGoogleMock(&argc, argv);
  // ... あなたのテストフレームワークで必要な処理を書きます ...
}

この方法には問題があります:Google Mock は,モックオブジェクトのデストラクタから時々例外を投げるようになります.コンパイラによっては,これによってテストプログラムが時々クラッシュします.テストの失敗に気づくことは可能ですが,綺麗な失敗ではありません.

より良い方法は,Google Test のイベントリスナー API を利用して,テストの失敗を Testing Framework に適切に伝えることです.イベントリスナーインタフェースの OnTestPartResult() メソッドを実装する必要がありますが,それは単純な作業です.

この作業が大変すぎると思うならば,やはり Google Mock (厳密に言えば,Google Mock の一部)とシームレスに動作する Google Test を使うことを勧めます.もし Google Test を利用できない理由があるならば,我々に連絡してください.

Expectation を設定する

モックオブジェクトをうまく利用するためには,適切な Expectation を設定することが重要です.Expectation を厳しくしすぎると,関係ない変更からも影響を受けてテストが失敗します.逆に Expectation を緩くしすぎると,バグが入り込みます.意図したバグを正確にキャッチできるように,テストが丁度良く動作してほしいことでしょう.Google Mock は,この「丁度良い」動作に必要な手段を提供します.

一般的な構文

Google Mock では,モックメソッドの Expectation を設定するために EXPECT_CALL() マクロを使います.一般的な構文は次のようになります:

EXPECT_CALL(mock_object, method(matchers))
    .Times(cardinality)
    .WillOnce(action)
    .WillRepeatedly(action);

このマクロには2つの引数があります:1番目はモックオブジェクトで,2番目はメソッドと引数です.これらは,ピリオド(.)ではなくカンマ(,)で区切られていることに注意してください(何故カンマを利用するのか?技術的な理由,というのがその答えです).

マクロに,Expectation に関する情報を与える節を追加することもできます.それぞれの節の働きについては,以降のセクションで議論することにします.

この構文は,Expectation が英語のように読めるように設計されています.例えば,次のようになっているとしましょう:

using ::testing::Return;...
EXPECT_CALL(turtle, GetX())
    .Times(5)
    .WillOnce(Return(100))
    .WillOnce(Return(150))
    .WillRepeatedly(Return(200));

turtle オブジェクトの GetX() メソッドが5回呼ばれ,最初は 100 を返し,次に 150,それ以降は 200 を返す,ということが想像できます.このような構文スタイルを,ドメイン固有言語(DSL)と呼ぶ人もいます.

注意: このようなことを行うのにマクロを利用しているのは何故でしょうか?これには2つの目的があります:1番目の目的は,(grep や 人間の読み手が)Expectation を簡単に識別できるようにすることです.そして2番目の目的は,Google Mock のメッセージに,失敗した Expectation がソースファイルのどこにあるかを含められるようにして,デバッグを簡単にすることです.

Matchers:期待する引数は何か?

引数を持つモック関数の場合,期待する引数を指定しなければいけません.例えば次のようになります.

// turtle が100単位 前進することを期待します.
EXPECT_CALL(turtle, Forward(100));

具体的過ぎる指定をしたくない場合もあるでしょう(テストが厳格すぎるかどうか,について述べていることに注意してください.過剰な指定は,テストを不安定に,テストの意図を不明確にします.よって,必要十分な指定のみを行うことをお勧めします).Forward() が呼ばれるかどうかだけをチェックしたく,実際の引数には関心がないならば,引数には「何でも良い」を意味する _ を書いてください.

using ::testing::_;
...
// turtle が前進することを期待します.
EXPECT_CALL(turtle, Forward(_));

_ は,matcher と呼ばれるもののインスタンスです.matcher は述語関数と同じように,引数が期待したものかどうかをテストするために利用できます.matcher は,EXPECT_CALL() 内部で,引数をとる関数が書かれてる場所でならどこでも利用できます.

組み込みの matcher 一覧は, チートシート にあります.ここでは,例として Ge(greater than or equal:以上) matcher を使ってみます:

using ::testing::Ge;...
EXPECT_CALL(turtle, Forward(Ge(100)));

これは,turtle が最低でも100単位前進するように命令されたことをチェックします.

Cardinalities:何回呼ばれるか?

EXPECT_CALL() の後ろに指定できる最初の節は,Times() です.この引数は,呼び出しが何回起こるかを説明するものなので,これを cardinality と呼びます.これを利用すると,実際に何度も書かなくても Expectation を繰り返すことができます.さらに重要なのは,ardinality は matcher と同じように「あいまい」な表現ができるということです.これによって,ユーザはテストの意図を正確に表現できます.

Times(0) は,興味深い特別なケースです.想像がつくかもしれませんが….この場合,関数は 1 度も呼ばれるべきではないので,関数が(不当にも)呼び出されると,Google Mock が Google Test に失敗を報告します.

あいまいな cardinality の例としては,先ほど AtLeast(n) を見ました.組み込みの cardinality の一覧は, チートシート にあります.

Times() 節は,省略することができます. Times() が省略されると, Google Mock は cardinality を推定します. そのルールは,覚えやすいものです:

  • WillOnce() または WillRepeatedly() の どちらも EXPECT_CALL() に 含まれない 場合,推定される carinality は Times(1) です.
  • WillOnce() が n 個(ここで n >= 1)あり,WillRepeatedly() が ない 場合,cardinality は Times(n) です.
  • WillOnce() が n 個(ここで n >= 0)あり,WillRepeatedly() も 1つある 場合,cardinality は Times(Atleast(n)) です.

確認クイズ: 2回の呼び出しが期待されている関数が,実際には4回呼び出されると何が起こると思いますか?

Actions:何をするべきなのか?

モックオブジェクトは,実際に動作する実装を持っているわけではないことを忘れないでください.ユーザは,メソッドが呼び出されたときに何をすればよいのかを指示する必要があります.Google Mock では,これを簡単に行えます.

まず,モック関数の戻り値の型が,組み込み型またはポインタである場合,関数の動作はデフォルト Action(void 関数は,単に関数から戻るだけです.bool 関数は false を返し,その他の関数は 0 を返します.)になります.特に指定しない場合,この動作が利用されます.

次に,モック関数がデフォルト Action を持たない場合,またはデフォルト Action では問題がある場合,WillOnce() 節の連続( WillRepeatedly() がその後ろに続く場合もあります)を利用して,Expectation がマッチするたびに起こされる Action を指定できます.

using ::testing::Return;...
EXPECT_CALL(turtle, GetX())
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillOnce(Return(300));

この場合, turtle.GetX() が丁度3回(Time() 明示されていないので,Google Mock は WillOnce() 節が書かれた回数から,これを推定します)呼び出され,100,200,300 がそれぞれ返されます.

using ::testing::Return;...
EXPECT_CALL(turtle, GetY())
    .WillOnce(Return(100))
    .WillOnce(Return(200))
    .WillRepeatedly(Return(300));

この場合, turtle.GetY() が最低でも2回(Times() が明示されず,2つの WillOnce() 節と1つの WillRepeatedly() が書かれているので,Google Mock の推定値はこうなります)呼び出され,最初は 100 ,2回目は 200 ,それ以降は 300 が返されます.

もちろん,Times() を明示すれば, Google Mock は cardinality を推定しようとはしません.指定した数が,WillOnce() 節の数よりも多い場合はどうなるでしょう?WillOnce() が使い切られた後,Google Mock は 関数のデフォルトアクションを毎回実行します(もちろん,WillRepeatedly() がある場合は除きます).

WillOnce() 内部では, Return() 以外に何ができるのでしょうか?例えば,ReturnRef(variable) を利用して参照を返したり,事前に定義された関数を呼び出し たり することができます.

重要な注意: Action は何回も実行されるかもしれませんが,EXPECT_CALL() 文は,Action 節を1度だけ評価します.したがって,副作用には十分に注意しなければいけません.例えば,以下のようにすると期待する動作にはならないでしょう:

int n = 100;
EXPECT_CALL(turtle, GetX())
.Times(4)
.WillOnce(Return(n++));

n++ は1度しか評価されないので,モック関数は常に 100 を返し,100,101,102 が連続して返されることはありません. 同様に, Return(new Foo) とすると,EXPECT_CALL() 実行時に Foo オブジェクトが作成され,同じポインタが毎回返されることになります. 副作用が毎回起こるようにしたいならば,カスタム Action を定義する必要があります.これについては, クックブック で説明します.

さてもう一度クイズの時間です!次のコードはどういう意味でしょうか?

using ::testing::Return;...
EXPECT_CALL(turtle, GetY())
.Times(4)
.WillOnce(Return(100));

turtle.GetY() が4回呼ばれることが期待されているのは明らかです.しかし,そこで毎回 100 が返されると思った人,もう一度考えてみてください.関数が呼び出されるたびに1つの WillOnce() 節が消費され,その後はデフォルト Action が実行されます.つまり, turtle.GetY() は最初に 100 を返し,2回目からは 0 が返される,というのが正解です.int 関数のデフォルト Action が 0 を返すことだからです.

複数の Expectation を利用する

今までは,1つの Expectation を利用する例だけを見てきました.より現実的に考えると,複数のモックオブジェクトにある複数のモックメソッドの Expectation を指定することになります.

Google Mock のデフォルトでは,モックメソッドが呼び出されると,引数にマッチするアクティブな Expectation が見つかるまで,定義順とは逆方向に Expectation を探索します(「古いルールは新しいルールに上書きされる」と考えることができます).マッチした Expectation に,有効な呼び出しが残っていなければ,上限違反による失敗が発生します.

using ::testing::_;...
EXPECT_CALL(turtle, Forward(_));  // #1
EXPECT_CALL(turtle, Forward(10))  // #2
    .Times(2);

もし Forward(10) が3回連続で呼び出されると,最後にマッチする Expectation (#2) は,既に飽和状態なので,3回目の呼び出しはエラーになります.しかし,もし Forward(10) の3回目の呼び出しが Forward(20) となっていたなら,これは #1 にマッチするので問題ありません.

傍注: Google Mock は,どうして Expectation を逆順に探索するのでしょうか?こうすることで,ユーザは,モックオブジェクトのコンストラクタ,またはテストフィクスチャの set-up メソッド内でデフォルト Expectation を設定して,テスト本体に,より具体的な Expectation を書いてモックをカスタマイズできるからです.そして同じメソッドに 2つの Expectation がある場合,より具体的な matcher を持つ方を後ろに書くか,より具体的なルールを持つ方を一般的なルールを持つ方の後ろに書きます.

順序あり呼び出し と 順序なし呼び出し

デフォルトでは,前に指定された Expectation が満足されていなくても,後ろで指定された Expectation が呼び出しにマッチできます.別の言い方をすれば,Expectation が指定された順序で呼び出しが起こる必要は無い,ということです.

期待される呼び出しがすべて,ある順序どおりに発生してほしい場合があるかもしれません.Google Mock でこれを実現するのは簡単です:

using ::testing::InSequence;...
TEST(FooTest, DrawsLineSegment) {
  ...
 {
    InSequence dummy;

    EXPECT_CALL(turtle, PenDown());
    EXPECT_CALL(turtle, Forward(100));
    EXPECT_CALL(turtle, PenUp());
  }
  Foo();
}

InSequence 型のオブジェクトを作ることで,そのスコープ内の全ての Expectation がシーケンスに並び,その順序どおりに呼び出しが発生することが必要になります.実際の動作部分は,このオブジェクトのコンストラクタとデストラクタだけに依存しているので,その名前は何でもかまいません.

この例では,Foo() が,3つの関数を書かれた順序どおりに呼び出すことをテストします.もし呼び出しの順序が違えば,エラーが起こります.

(呼び出し全体の順序ではなく,呼び出しの一部の相対的な順番だけが問題になる場合はどうでしょう?任意の部分の順序を指定することができるのでしょうか?答えは…できます!待ちきれないならば,この詳細は クックブック に書かれています.)

全ての Expectation は Sticky (そうではないと明示される場合は除外)

さて,このモックをどの程度使いこなせるかを測るクイズをしましょう.turtle が原点に向かって進むように,丁度2回命令されたかどうか(その他の命令は全て無視したい)をテストするには,どうすれば良いでしょうか?

自分で答えを考えてから,下の例と比べてみてください(まず,自分で解いてください.ズルはしないように!):

using ::testing::_;...
EXPECT_CALL(turtle, GoTo(_, _))  // #1
    .Times(AnyNumber());
EXPECT_CALL(turtle, GoTo(0, 0))  // #2
    .Times(2);

turtle.GoTo(0, 0) が3回呼び出されるとします.3回目の呼び出しで,Google Mock は,その引数が Expectation #2 にマッチするのを検出します(常に,最後にマッチした Expectation が選択されることを忘れないでください).そのような呼び出しは2回だけ,と期待しているので,Google Mock は即座にエラーを報告します.これが,上述の「 複数の Expectation を利用する 」のセクションで説明した基本的な動作です.

この例は,Google Mock の Expectation がデフォルトでは,「sticky」であることを表しています. つまり,呼び出し回数が上限に達してもアクティブであり続ける,ということを意味します.これは,仕様の意味に影響を与え,他の mocking framework の動作とは異なる部分なので,忘れてはならない重要なルールです.

簡単ですか?本当に理解したかどうか確認してみましょう:以下のコードの意味は何でしょうか?

using ::testing::Return;
...
for (int i = n; i > 0; i--) {
  EXPECT_CALL(turtle, GetX())
      .WillOnce(Return(10*i));
}

turtle.GetX() が n 回呼び出されて, 10,20,30,…,が連続して返される,と思った人は,もう一度考えてください.問題は,先ほど述べたように Expectation が sticky である,ということです.2回目の turtle.GetX() が呼び出されると,最後(最新)の EXPECT_CALL() 文がマッチし,すぐに「上限超過」エラーが起こります.つまり,このコードはあまり役に立ちません!

turtle.GetX() が 10,20,30,…,を返すような正しい方法は,Expectation が sticky ではないことを明示することです.言い方を変えれば,飽和したらすぐに破棄されるべきだ,ということです.

using ::testing::Return;
...
for (int i = n; i > 0; i--) {
  EXPECT_CALL(turtle, GetX())
    .WillOnce(Return(10*i))
    .RetiresOnSaturation();
}

そして,さらに適切な方法があります:この場合,呼び出しが特定の順序で起こることを期待して,マッチする順序に Action を並べます.ここでは順序が重要なので,シーケンスを用いてそれを明示するべきです:

using ::testing::InSequence;
using ::testing::Return;
...
{
  InSequence s;

  for (int i = 1; i <= n; i++) {
    EXPECT_CALL(turtle, GetX())
        .WillOnce(Return(10*i))
        .RetiresOnSaturation();
  }
}

ところで,Expectation が sticky にならないもう1つの状況は,シーケンスに並んだときです.自分より後ろの Expectation が利用されるとすぐに,それは自動的に破棄されます(どの呼び出しにもマッチしなくなります).

不要な呼び出し

モックオブジェクトには,多くのメソッドが含まれる場合がありますが,その全てに関心があるわけではありません.例えば,あるテストでは,GetX() や GetY() が何回呼び出されるかを気にする必要がないかもしれません.

Google Mock では,あるメソッドが不要な場合,何もする必要がありません.そのメソッドが呼び出されると,テストの出力に警告が表示されますが,これは失敗を意味するものではありません.

次のステップ

おめでとうございます!Google Mock を使い始める準備が整いました.早速, googlemock ディスカッショングループに参加したい,実際に Google Mock を利用したテストを書いてみたい,と考えるかもしれません.この作業は楽しくて,病み付きになる可能性すらあります.– ちゃんと忠告しましたよ.

いろいろなモックを書いたなぁ,と思ったら クックブック を読んでみるべきです.Google Mock の高度な機能を多く学ぶことができ,楽しみがさらに広がります.