動機
前回の記事でGooglemockの簡単な使い方を紹介したが、今回はMockをもっと活用する方法を紹介する。
SaveArgとは
SaveArgはEXPECT_CALLの後につけられる関数で、呼ばれる事が期待される関数に与えられた引数へのポインタを保存することができる。これにより調べる関数内でMockされたクラスに与えられた引数を知ることができる。
例
前回のサンプルを一部変更して、doSomethingに与えられた引数をSaveArgでdataに保存し、40であることを期待するようにするEXPECTを書く
class IInjected{
public:
virtual ~IInjected() = default;
virtual int doSomething(int x) = 0;
};
class InjectedMock : public IInjected{
public:
InjectedMock(){}
MOCK_METHOD1(doSomething, int(int));
};
class Target{
public:
Target(std::unique_ptr<IInjected> injected):injected_(std::move(injected)){}
void doAnything(){
auto y = injected_->doSomething(40);
}
private:
std::unique_ptr<IInjected> injected_;
};
TEST(test, test){
int data;
std::unique_ptr<InjectedMock> injectionMock = std::make_unique<InjectedMock>();
EXPECT_CALL(*injectionMock, doSomething(testing::_)).Times(1).WillOnce(testing::SaveArg<0>(&data));
auto&& target = Target(std::move(injectionMock));
target.doAnything();
EXPECT_EQ(data, 40);
}
testing::_はプレースホルダーでどんな引数が来てもOKであることを意味する。この例だと単純に
EXPECT_CALL(*injectionMock, doSomething(40)).Times(1)
でも同じことだが、引数が特定の範囲内であるとか、より条件が複雑な場合にはSaveArgが役に立つ。だが、一番役に立つときはstd::function、つまりlambdaが引数として与えられている時である。
LambdaとSaveArg<T>
ラムダの中に定義された関数オブジェクトをテストしたいとき、そしてその関数オブジェクトを与える先がMockできるとき、SaveArg<T>により関数オブジェクトへのポインタをTargetオブジェクトの外に引っ張ることができる。
class IInjected{
public:
virtual ~IInjected() = default;
virtual void doSomething(std::function<int(int)> func) = 0;
};
class InjectedMock : public IInjected{
public:
InjectedMock(){}
MOCK_METHOD1(doSomething, void(std::function<int(int)>));
};
class Target{
public:
Target(std::unique_ptr<IInjected> injected):injected_(std::move(injected)){}
void doAnything(){
injected_->doSomething([](int x) -> int{
return x * x;
});
}
private:
std::unique_ptr<IInjected> injected_;
};
TEST(test, test){
std::function<int(int)> lambda;
std::unique_ptr<InjectedMock> injectionMock = std::make_unique<InjectedMock>();
EXPECT_CALL(*injectionMock, doSomething(testing::_)).Times(1).WillOnce(testing::SaveArg<0>(&lambda));
auto&& target = Target(std::move(injectionMock));
target.doAnything();
EXPECT_EQ(lambda(5), 25);
}