googlemockのつかいかたまとめ

http://src.chromium.org/svn/trunk/src/remoting/client/chromoting_view_unittest.cc
ここらへんとかを読み解きながら。

基本の書き方

class Hoge{
public:
    Hoge(){};
    ~Hoge(){};

public:
    int hogeMethod1(int x){ return x;};
};

というクラスがあった時は、まずモッククラスを作成する。

using ::testing::Mock;
using ::testing::_;
using ::testing::Invoke;
using ::testing::Return;

class MockHoge : public Hoge{
public:
    MockHoge(){
        //モックのメソッドが呼び出されたときに、Hogeクラスの対応するメソッドを呼び出すように設定する(書かなくてもよい)
        ON_CALL(*this, hogeMethod1(_)).WillByDefault(Invoke(&hoge_, &Hoge::hogeMethod1));
    };
    //デコンストラクタはvirtualにすると書いてあったけどしなくてもよいかも?
    virtual ~MockHoge(){};

    //引数が0だったらMOCK_METHOD0, 1だったらMOCK_METHOD1,...と書いていく
    //第1引数はメソッド名、第二引数は"戻り値(引数)"
  MOCK_METHOD1(hogeMethod1, int(int));

private:
    Hoge hoge_;  //ON_CALL用。コンストラクタで書かない場合はいらない。
};

テストメソッドは以下のようにかく。

//TEST(任意、任意)
TEST(MockHogeTest, invokeTest){
    MockHoge mh;
    //引数が何であってもよい。1回呼ばれたらsuccessを返してくれる
    EXPECT_CALL(mh, hogeMethod1(_)).Times(1);
    EXPECT_EQ(1, mh.hogeMethod1(1));

    //引数指定、WillOnce(Return(戻り値))で戻り値が本来かえってくるものとは別のものを指定することも可
    EXPECT_CALL(mh, hogeMethod1(1)).Times(1).WillOnce(Return(10));
    EXPECT_EQ(10, mh.hogeMethod1(1));
}

あるメソッドの中で同じクラスのメソッドが呼び出されることがあって、それをテストしたい場合はやり方が少し違うので注意。
例えばこんなクラスがあったとする。

class Calc{
public:
    Calc(){};
    ~Calc(){};

    int shiftUpNBit(int x, int n){ return x << n; };
    int shiftDownNBit(int x, int n){ return x << n; };
    bool isEven(int x){ return x % 2 == 0; };
    //偶数だったらnビット左にシフト、奇数だったらnビット右にシフトする
    int calc(int x, int n){ return isEven(x) ? shiftUpNBit(x, n) : shigtDownNBit(x, n); };
};

calcを呼ぶと中でCalcクラスのshiftUpNBit, shiftDownNBit, isEvenが呼ばれる。全部pubicにしてるけど…

class MockCalc : public Calc{
public:
    MockCalc(){
        ON_CALL(*this, shiftUpNBit(_, _)).WillByDefault(Invoke(&calc_, &Calc::shiftUpNBit));
        ON_CALL(*this, shiftDownNBit(_, _)).WillByDefault(Invoke(&calc_, &Calc::shiftDownNBit));
        ON_CALL(*this, isEven(_)).WillByDefault(Invoke(&calc_, &Calc::isEven));
        ON_CALL(*this, calc(_, _)).WillByDefault(Invoke(&calc_, &Calc::calc));
    };
    virtual ~MockCalc(){};

public:
    MOCK_METHOD2(shiftUpNBit, int(int, int));
    MOCK_METHOD2(shitDownNBit, int(int, int));
    MOCK_METHOD1(isEven, bool(int));
    MOCK_METHOD1(calc, int(int, int));

private:
    Calc calc_;
};

ここまではその上とおんなじだが。

TEST(CalcTest, testCalcInvoke){
    //ヒープ領域に取らないとエラーが出るぽい
    MockCalc* mc = new MockCalc();
    {
        //順番のテストとメソッドの中でちゃんとメソッドが呼び出されているかテストする(EXPECT_CALLはまちがいでON_CALLが正しいです;#2011/2/17)
        InSequence s;
        EXPECT_CALL(*mc, calc(_, _));
        EXPECT_CALL(*mc, isEven(_)).WillOnce(Return(true));  //どんな場合もtrueがかえる
        EXPECT_CALL(*mc, shiftUpNBit(_, _));
    }
    EXPECT_EQ(4, mc->calc(1, 2));

    //モックオブジェクトは勝手に解放されないので、メモリーリークするかもしれないというWarningが出力される
    //表示させたくない場合はMock::AllowLeakを使う
    MOCK::AllowLeak(&mc);
    //モックオブジェクトを初期化する
    Mock::VerifyAndClear(&mc);
    //普通に解放してもよい。
    mc->~MockCalc();
  //delete mc; //こちらでもよい。
}