文章目录
-
1.自定义方法/成员函数的期望行为
-
2.匹配器
-
3.基数Cardinalities
-
4.行为(Actions)
-
5.序列(Sequences)
-
6.基于虚函数的Mock实战
-
7.Nice Mocks 和 Strict Mocks
-
8.ON_CALL与EXPECT_CALL区别
-
1.Cardinalities: How Many Times Will It Be Called?
-
2.Matchers: What Arguments Do We Expect?
-
3.Actions: What Should It Do?
-
4.Using Multiple Expectations
-
5.Ordered vs Unordered Calls
-
6.All Expectations Are Sticky
-
1.Creating Mock Classes
-
2.Mocking Private or Protected Methods
-
3.Mocking Overloaded Methods
-
4.Mocking Class Templates
-
5.Mocking Non-virtual Methods
-
6.Mocking Free Functions
-
7.Old-Style MOCK_METHODn Macros
一、如何使用gMock
1 首先,使用一些简单的宏来描述要模拟的接口,它们将扩展到模拟类的实现;
2 接下来,创建一些模拟对象,并使用直观的语法指定它的期望和行为;
3.然后练习使用模拟对象的代码。一旦出现任何违反预期的情况,gMock将立即捕获。
mock的语法见:docs/reference/mocking.md
1.自定义方法/成员函数的期望行为
-
ref:转一篇小亮同学的google mock分享
对方法期望行为的定义的语法格式如下:
EXPECT_CALL(mock_object, method(matcher1, matcher2, ...))
.With(multi_argument_matcher)
.Times(cardinality)
.InSequence(sequences)
.After(expectations)
.WillOnce(action)
.WillRepeatedly(action)
.RetiresOnSaturation();
第1行的mock_object就是你的Mock类的对象
第1行的method(matcher1, matcher2, …)中的method就是你Mock类中的某个方法名,比如上述的getArbitraryString;而matcher(匹配器)的意思是定义方法参数的类型,我们待会详细介绍。
第3行的Times(cardinality)的意思是之前定义的method运行几次。至于cardinality的定义,我也会在后面详细介绍。
第4行的InSequence(sequences)的意思是定义这个方法被执行顺序(优先级),我会再后面举例说明。
第6行WillOnce(action)是定义一次调用时所产生的行为,比如定义该方法返回怎么样的值等等。
第7行WillRepeatedly(action)的意思是缺省/重复行为。
EXPECT_CALL(mockTurtle, getX()).Times(testing::AtLeast(5)).
WillOnce(testing::Return(100)).WillOnce(testing::Return(150)).
WillRepeatedly(testing::Return(200))
调用mockTurtle的getX()方法
这个方法会至少调用5次
第一次被调用时返回100
第2次被调用时返回150
从第3次被调用开始每次都返回200
2.匹配器
Matcher用于定义Mock类中的方法的形参的值(当然,如果你的方法不需要形参时,可以保持match为空。),它有以下几种类型
通配符
-
这里的_和*A*包括下面的那个匹配符都在Google Mock的*::testing*这个命名空间下,大家要用时需要先引入那个命名空间
一般比较
浮点数的比较
字符串匹配
-
这里的字符串即可以是C风格的字符串,也可以是C++风格的。
容器的匹配
-
很多STL的容器的比较都支持==这样的操作,对于这样的容器可以使用上述的Eq(container)来比较。但如果你想写得更为灵活,可以使用下面的这些容器匹配方法:
-
eg:
#ifndef MOCKFOO_H_
#define MOCKFOO_H_
#include <gmock/gmock.h>
#include <string>
#include <vector>
#include "FooInterface.h"
namespace seamless {
class MockFoo: public FooInterface {
public:
MOCK_METHOD(std::string, getArbitraryString, ());
MOCK_METHOD1(void, setValue, (std::string& value));
MOCK_METHOD2(void, setDoubleValues, (int x, int y));
} // namespace seamless
#endif // MOCKFOO_H_
#include <cstdlib>
#include <gmock/gmock.h>
#include <iostream>
#include <string>
#include "MockFoo.h"
using namespace seamless;
using namespace std;
using ::testing::Assign;
using ::testing::Eq;
using ::testing::Ge;
using ::testing::Return;
int main(int argc, char** argv) {
::testing::InitGoogleMock(&argc, argv);
string value = "Hello World!";
MockFoo mockFoo;
EXPECT_CALL(mockFoo, setValue(testing::_));
mockFoo.setValue(value);
// 这里我故意犯错
EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)));
mockFoo.setDoubleValues(1, 0);
return EXIT_SUCCESS;
}
第22行,让setValue的形参可以传入任意参数
另外,我在第26~27行故意犯了个错(为了说明上述这些匹配器的作用),我之前明明让setDoubleValues第二个参数得大于等于1,但我实际传入时却传入一个0。这时程序运行时就报错了:
unknown file: Failure
Unexpected mock function call – returning directly.
Function call: setDoubleValues(1, 0)
Google Mock tried the following 1 expectation, but it didn't match:
FooMain.cc:35: EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)))…
Expected arg #1: is >= 1
Actual: 0
Expected: to be called once
Actual: never called – unsatisfied and active
FooMain.cc:35: Failure
Actual function call count doesn't match EXPECT_CALL(mockFoo, setDoubleValues(Eq(1), Ge(1)))…
Expected: to be called once
Actual: never called – unsatisfied and active
成员匹配器
-
eg:第5行,我们定义了一个Field(&Bar::num, Ge(0)),以说明Bar的成员变量num必须大于等于0。
TEST(TestField, Simple) {
MockFoo mockFoo;
Bar bar;
EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0)))).Times(1);
mockFoo.get(bar);
int main(int argc, char** argv) {
::testing::InitGoogleMock(&argc, argv);
return RUN_ALL_TESTS();
}
-
eg:我们为了说明Field的作用,传入一个bar.num = -1试试。
TEST(TestField, Simple) {
MockFoo mockFoo;
Bar bar;
bar.num = -1;
EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0)))).Times(1);
mockFoo.get(bar);
}
[==========] Running 1 test from 1 test case.
[----------] Global test environment set-up.
[----------] 1 test from TestField
[ RUN ] TestField.Simple
unknown file: Failure
Unexpected mock function call – returning directly.
Function call: get(@0xbff335bc 4-byte object )
Google Mock tried the following 1 expectation, but it didn't match:
FooMain.cc:34: EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0))))…
Expected arg #0: is an object whose given field is >= 0
Actual: 4-byte object , whose given field is -1
Expected: to be called once
Actual: never called – unsatisfied and active
FooMain.cc:34: Failure
Actual function call count doesn't match EXPECT_CALL(mockFoo, get(Field(&Bar::num, Ge(0))))…
Expected: to be called once
Actual: never called – unsatisfied and active
[ FAILED ] TestField.Simple (0 ms)
[----------] 1 test from TestField (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test case ran. (0 ms total)
[ PASSED ] 0 tests.
[ FAILED ] 1 test, listed below:
[ FAILED ] TestField.Simple
1 FAILED TEST
匹配函数或函数对象的返回值
指针匹配器
复合匹配器
EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10))));
EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), NULL));
3.基数Cardinalities
基数用于Times()中来指定模拟函数将被调用多少次
4.行为(Actions)
Actions(行为)用于指定Mock类的方法所期望模拟的行为:比如返回什么样的值、对引用、指针赋上怎么样个值,等等。
-
使用函数或者函数对象(Functor)作为行为
-
复合动作
-
eg:例如有一个Mock方法:
irtual int getParamter(std::string* name, std::string* value) = 0
对于这个方法,我这回需要操作的结果是将name指向value的地址,并且得到方法的返回值。
类似这样的需求,我们就可以这样定义期望过程:
-
这时就用上了我们的DoAll()了,它将Assign()和Return()结合起来了。
TEST(SimpleTest, F1) {
std::string* a = new std::string("yes");
std::string* b = new std::string("hello");
MockIParameter mockIParameter;
EXPECT_CALL(mockIParameter, getParamter(testing::_, testing::_)).Times(1).\
WillOnce(testing::DoAll(testing::Assign(&a, b), testing::Return(1)));
mockIParameter.getParamter(a, b);
}
5.序列(Sequences)
默认时,对于定义要的期望行为是无序(Unordered)的,即当我定义好了如下的期望行为:
-
eg:对于这样的期望行为的定义,我何时调用mockFoo.getValue()或者何时mockFoo.getSize()都可以的。
MockFoo mockFoo;
EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));
但有时候我们需要定义有序的(Ordered)的调用方式,即序列 (Sequences) 指定预期的顺序. 在同一序列里的所有预期调用必须按它们指定的顺序发生; 反之则可以是任意顺序.
using ::testing::Return;
using ::testing::Sequence;
int main(int argc, char **argv) {
::testing::InitGoogleMock(&argc, argv);
Sequence s1, s2;
MockFoo mockFoo;
EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2).WillOnce(Return(1));
EXPECT_CALL(mockFoo, getValue()).InSequence(s1).WillOnce(Return(
string("Hello World!")));
cout << "First:\t" << mockFoo.getSize() << endl;
cout << "Second:\t" << mockFoo.getValue() << endl;
return EXIT_SUCCESS;
}
-
首先在第8行建立两个序列:s1、s2。
-
然后在第11行中,EXPECT_CALL(mockFoo, getSize()).InSequence(s1, s2)说明getSize()的行为优先于s1、s2.
-
而第12行时,EXPECT_CALL(mockFoo, getValue()).InSequence(s1)说明getValue()的行为在序列s1- 中。
-
结果:
First: 1
Second: Hello World!
-
eg:把mockFoo.getSize()和mockFoo.getValue()的调用对调时试试:
cout << "Second:\t" << mockFoo.getValue() << endl;
cout << "First:\t" << mockFoo.getSize() << endl;
unknown file: Failure
Unexpected mock function call – returning default value.
Function call: getValue()
Returns: ""
Google Mock tried the following 1 expectation, but it didn't match:
FooMain.cc:29: EXPECT_CALL(mockFoo, getValue())…
Expected: all pre-requisites are satisfied
Actual: the following immediate pre-requisites are not satisfied:
FooMain.cc:28: pre-requisite #0
(end of pre-requisites)
Expected: to be called once
Actual: never called – unsatisfied and active
Second:
First: 1
FooMain.cc:29: Failure
Actual function call count doesn't match EXPECT_CALL(mockFoo, getValue())…
Expected: to be called once
Actual: never called – unsatisfied and active
另外,我们还有一个偷懒的方法,就是不要这么傻乎乎地定义这些个Sequence s1, s2的序列,而根据我定义期望行为(EXPECT_CALL)的顺序而自动地识别调用顺序,这种方式可能更为地通用。
using ::testing::InSequence;
using ::testing::Return;
int main(int argc, char **argv) {
::testing::InitGoogleMock(&argc, argv);
InSequence dummy;
MockFoo mockFoo;
EXPECT_CALL(mockFoo, getSize()).WillOnce(Return(1));
EXPECT_CALL(mockFoo, getValue()).WillOnce(Return(string("Hello World")));
cout << "First:\t" << mockFoo.getSize() << endl;
cout << "Second:\t" << mockFoo.getValue() << endl;
return EXIT_SUCCESS;
}
6.基于虚函数的Mock实战
VariantField.h
#ifndef VARIANTFIELD_H_
#define VARIANTFIELD_H_
#include <boost/cstdint.hpp>
namespace seamless
union VariantField
const char *strVal;
int32_t intVal;
} // namespace mlr_isearch_api
#endif // VARIANTFIELD_H_
IAPIProviderInterface.h
#ifndef IAPIPROVIDERINTERFACE_H_
#define IAPIPROVIDERINTERFACE_H_
#include <boost/cstdint.hpp>
#include "IParameterInterface.h"
#include "VariantField.h"
namespace seamless
class IAPIProviderInterface
public:
IAPIProviderInterface() {}
virtual ~IAPIProviderInterface() {}
public:
virtual IParameterInterface *getParameterInterface() = 0;
#endif // IAPIPROVIDERINTERFACE_H_
IParameterInterface.h
#ifndef IPARAMETERINTERFACE_H_
#define IPARAMETERINTERFACE_H_
#include <boost/cstdint.hpp>
#include "VariantField.h"
namespace seamless
class IParameterInterface
public:
virtual ~IParameterInterface(){};
public:
virtual int32_t getParameter(const char *name, VariantField *&value) = 0;
} // namespace
#endif // IPARAMETERINTERFACE_H_
MockIAPIProviderInterface.h
#ifndef MOCKIAPIPROVIDERINTERFACE_H_
#define MOCKIAPIPROVIDERINTERFACE_H_
#include <gmock/gmock.h>
#include "IAPIProviderInterface.h"
#include "IParameterInterface.h"
namespace seamless
class MockIAPIProviderInterface : public IAPIProviderInterface
public:
MOCK_METHOD(IParameterInterface *, getParameterInterface, ());
} // namespace seamless
#endif // MOCKIAPIPROVIDERINTERFACE_H_
MockIParameterInterface.h
#ifndef MOCKIPARAMETERINTERFACE_H_
#define MOCKIPARAMETERINTERFACE_H_
#include <boost/cstdint.hpp>
#include <gmock/gmock.h>
#include "IParameterInterface.h"
#include "VariantField.h"
namespace seamless
class MockIParameterInterface : public IParameterInterface
public:
MOCK_METHOD(int32_t, getParameter, (const char *name, VariantField *&value));
} // namespace seamless
#endif // MOCKIPARAMETERINTERFACE_H_
Rank.cc
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include "IAPIProviderInterface.h"
#include "IParameterInterface.h"
#include "VariantField.h"
#include "Rank.h"
using namespace seamless;
using namespace std;
namespace seamless
void Rank::processQuery(IAPIProviderInterface *iAPIProvider)
IParameterInterface *iParameter = iAPIProvider->getParameterInterface();
if (!iParameter)
cerr << "iParameter is NULL" << endl;
return;
int32_t isRetailWholesale = 0;
int32_t isUseAlipay = 0;
VariantField *value = new VariantField;
iParameter->getParameter("retail_wholesale", value);
isRetailWholesale = (strcmp(value->strVal, "0")) ? 1 : 0;
iParameter->getParameter("is_use_alipay", value);
isUseAlipay = (strcmp(value->strVal, "0")) ? 1 : 0;
cout << "isRetailWholesale:\t" << isRetailWholesale << endl;
cout << "isUseAlipay:\t" << isUseAlipay << endl;
delete value;
delete iParameter;
} // namespace seamless
Rank.h
#ifndef RANK_H_
#define RANK_H_
#include "IAPIProviderInterface.h"
namespace seamless
class Rank
public:
virtual ~Rank() {}
public:
void processQuery(IAPIProviderInterface *iAPIProvider);
} // namespace seamless
#endif // RANK_H_
tester.cc
#include <boost/cstdint.hpp>
#include <boost/shared_ptr.hpp>
#include <cstdlib>
#include <gmock/gmock.h>
#include "MockIAPIProviderInterface.h"
#include "MockIParameterInterface.h"
#include "Rank.h"
using namespace seamless;
using namespace std;
using ::testing::_;
using ::testing::AtLeast;
using ::testing::DoAll;
using ::testing::Return;
using ::testing::SetArgumentPointee;
int main(int argc, char **argv)
::testing::InitGoogleMock(&argc, argv);
MockIAPIProviderInterface *iAPIProvider = new MockIAPIProviderInterface;
MockIParameterInterface *iParameter = new MockIParameterInterface;
// 定义MockIAPIProviderInterface.getParameterInterface的的行为:程序至少被调用一次(Times(AtLeast(1))),每次调用都返回一个iParameter(即MockIParameterInterface*的对象)。
EXPECT_CALL(*iAPIProvider, getParameterInterface()).Times(AtLeast(1)).WillRepeatedly(Return(iParameter));
// 假设了一些Query的Segment的值
boost::shared_ptr<VariantField> retailWholesaleValue(new VariantField);
retailWholesaleValue->strVal = "0";
boost::shared_ptr<VariantField> defaultValue(new VariantField);
defaultValue->strVal = "9";
// 定义MockIParameterInterface.getParameter的期望行为:这个方法至少被调用一次;第一次被调用时返回1并将第一个形参指向retailWholesaleValue;后续几次被调用时返回1,并指向defaultValue。
EXPECT_CALL(*iParameter, getParameter(_, _)).Times(AtLeast(1)).WillOnce(DoAll(SetArgumentPointee<1>(*retailWholesaleValue), Return(1))).WillRepeatedly(DoAll(SetArgumentPointee<1>(*defaultValue), Return(1)));
// 运行Rank类下的processQuery方法。
Rank rank;
rank.processQuery(iAPIProvider);
delete iAPIProvider;
return EXIT_SUCCESS;
}
7.Nice Mocks 和 Strict Mocks
Foo.h 带private方法的接口
class Foo {
private:
virtual void setValue(int value) {};
public:
int value;
};
MockFoo.h
class MockFoo: public Foo {
public:
MOCK_METHOD1(setValue, void(int value));
};
当在调用Mock类的方法时,如果之前没有使用EXPECT_CALL来定义该方法的期望行为时,Google Mock在运行时会给你一些警告信息:
GMOCK WARNING:
Uninteresting mock function call – returning default value.
Function call: setValue(1)
Returns: 0
Stack trace
对于这种情况,可以使用NiceMock来避免:
using ::testing::NiceMock;
using ::testing::NaggyMock;
using ::testing::StrictMock;
// MockFoo mockFoo;
NiceMock<MockFoo> mockFoo;
当然,另外还有一种办法,就是使用StrictMock来将这些调用都标为失败:
StrictMock<MockFoo> mockFoo;
这时得到的结果:
unknown file: Failure
Uninteresting mock function call – returning default value.
Function call: setValue(1)
Returns: 0
8.ON_CALL与EXPECT_CALL区别
如果你确定这些调用是正确的。这将告诉gMock您确实期望调用,并且不应该打印任何警告。
-
写法不同
-
The mock function has no default action set, and its return type has no default value set,则使用ON_CALL
using ::testing::_;
EXPECT_CALL(foo, Bar(_))
.WillRepeatedly(...);
instead of
using ::testing::_;
ON_CALL(foo, Bar(_))
.WillByDefault(...);
二、实战
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;
};
编写Mock类
#include "gmock/gmock.h" // Brings in gMock.
class MockTurtle : public Turtle {
public:
MOCK_METHOD(void, PenUp, (), (override));
MOCK_METHOD(void, PenDown, (), (override));
MOCK_METHOD(void, Forward, (int distance), (override));
MOCK_METHOD(void, Turn, (int degrees), (override));
MOCK_METHOD(void, GoTo, (int x, int y), (override));
MOCK_METHOD(int, GetX, (), (const, override));
MOCK_METHOD(int, GetY, (), (const, override));
};
使用Mock
·1从测试名称空间导入gMock名称,以便可以不限定地使用它们(每个文件只需执行一次)。记住,名称空间是个好主意。
2.创建一些模拟对象。
3.指定您对它们的期望(一个方法将被调用多少次?用什么参数?它应该做什么?等等)。
4.练习一些使用模拟的代码;可选地,使用检查结果
5.googletest断言。如果一个模拟方法被调用的次数超过预期,或者使用了错误的参数,您将立即得到一个错误。
6.当一个mock被销毁时,gMock将自动检查对它的所有期望是否已得到满足。
```cpp
#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
}
这个测试检查至少调用了一次PenDown()。如果painter对象没有调用这个方法,你的测试就会失败,并显示如下信息:
path/to/my_test.cc:119: Failure
Actual function call count doesn't match this expectation:
Actually: never called;
Expected: called at least once.
Stack trace:
...
给Mock设置EXPECT_CALL
宏有两个参数:第一个是模拟对象,然后是方法及其参数参数。注意,两者之间用逗号(,)隔开,而不是句号(.)。如果方法没有重载,宏也可以在没有匹配器的情况下被调用
在gMock中,我们使用EXPECT_CALL()宏来设置模拟方法的期望。一般语法是:
1.Cardinalities: How Many Times Will It Be Called?
在EXPECT_CALL()之后可以指定的第一个子句是Times()。我们将其参数称为基数,因为它告诉调用应该进行多少次
/*
Cardinalities:即.Times()
Times()子句可以省略。如果省略Times(), gMock将推断你的基数。
如果WillOnce()和willrepeated()都不在, EXPECT_CALL(),推断的基数是Times(1)。
如果有n个WillOnce()但没有willrepeated(),其中n >=1,基数为Times(n)。
如果有n个WillOnce()和一个willrepeated(),其中n >=0,基数为Times(AtLeast(n))。
EXPECT_CALL(mock_object, method(matchers))
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);
EXPECT_CALL(mock_object, non-overloaded-method)
.Times(cardinality)
.WillOnce(action)
.WillRepeatedly(action);
它表示turtle对象的GetX()方法将被调用五次第一次返回100,第二次返回150,然后每次返回200。*/
EXPECT_CALL(turtle, GetX())
.Times(5)
.WillOnce(Return(100))
.WillOnce(Return(150))
.WillRepeatedly(Return(200));
2.Matchers: What Arguments Do We Expect?
当mock函数接受实参时,我们可以指定期望的实参
补充:非重载方法的Mock,若下面的接口不是virtual的
// Expects the turtle to move forward by 100 units.
EXPECT_CALL(turtle, Forward(100));
_ as the argument, which means "anything goes":
// Expects that the turtle jumps to somewhere on the x=50 line.
EXPECT_CALL(turtle, GoTo(50, _));
// Expects the turtle moves forward by at least 100.
Ge是Gtest内置的Matchers
Matchers:reference/matchers.md
EXPECT_CALL(turtle, Forward(Ge(100)));
//如果你不关心任何参数,而不是为每个参数指定_,你可以省略形参列表:
// Expects the turtle to move forward.
EXPECT_CALL(turtle, Forward);
// Expects the turtle to jump somewhere.
EXPECT_CALL(turtle, GoTo);
3.Actions: What Should It Do?
首先,如果模拟函数的返回类型是内置类型或指针,该函数有一个默认动作(void函数将返回bool函数返回false,其他函数返回0),在c++ 11及以上版本中,一个模拟函数,其返回类型为default-constructible(即有一个默认构造函数)的默认动作为返回默认构造的值。如果你什么都不说,这种行为将被使用。
其次,如果模拟函数没有默认动作,或者默认动作不适合你,你可以指定每次要采取的行动使用一系列WillOnce()子句后跟一个可选WillRepeatedly ()
ReturnRef(variable)可以返回引用
/*
我们没有显式地编写Times()),并将分别返回100、200和300
using ::testing::Return;
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(100))
.WillOnce(Return(200))
.WillOnce(Return(300));
没有explicit Times()),前两次将分别返回100和200,从第三次开始是300。
EXPECT_CALL(turtle, GetY())
.WillOnce(Return(100))
.WillOnce(Return(200))
.WillRepeatedly(Return(300));
注意:EXPECT_CALL()语句只计算action子句一次,即使该操作可能被执行多次。所以你一定要注意副作用。
using ::testing::Return;
int n = 100;
EXPECT_CALL(turtle, GetX())
.Times(4)
.WillRepeatedly(Return(n++));
这里不是返回100 101 102,…连续地,这个模拟函数会总是返回100,就像n + +只被评估过一次
using ::testing::Return;
EXPECT_CALL(turtle, GetY())
.Times(4)
.WillOnce(Return(100));
记住,每次调用函数时都会使用WillOnce()子句,然后执行默认操作。因此正确答案是turtle.GetY()第一次返回100,但第二次返回0,因为返回0是int函数的默认操作。
4.Using Multiple Expectations
默认情况下,当调用mock方法时,gMock将按照定义期望的相反顺序搜索期望,并在找到与参数匹配的活动期望时停止(您可以将其理解为“更新的规则覆盖旧的。”)。
using ::testing::_;
EXPECT_CALL(turtle, Forward(_)); // #1
EXPECT_CALL(turtle, Forward(10)) // #2
.Times(2);
如果Forward(10)连续被调用三次,那么第三次它将是错误,因为最后一个匹配期望(#2)已经饱和。然而,如果第三次Forward(10)调用被Forward(20)取代,那么它就OK了,现在第一个是匹配的期望。
5.Ordered vs Unordered Calls
在这个例子中,我们测试Foo()调用对象中的三个预期函数按写好的顺序。如果一个调用是无序的,它将是一个错误。
using ::testing::InSequence;
TEST(FooTest, DrawsLineSegment) {
InSequence seq;
EXPECT_CALL(turtle, PenDown());
EXPECT_CALL(turtle, Forward(100));
EXPECT_CALL(turtle, PenUp());
Foo();
}
6.All Expectations Are Sticky
gMock中的期望在默认情况下是“粘性的”,即使在我们达到它们的调用上限之后,它们仍然是活动的。这是一个需要记住的重要规则,因为它影响到规范的含义.
假设turtle.GoTo(0,0)被调用三次。在第三次中,gMock将执行确保参数与期望#2匹配(请记住,我们总是选择最后匹配期望)。既然我们说过只有两个这样的调用,gMock会立即报告错误。
using ::testing::_;
using ::testing::AnyNumber;
EXPECT_CALL(turtle, GoTo(_, _)) // #1
.Times(AnyNumber());
EXPECT_CALL(turtle, GoTo(0, 0)) // #2
.Times(2);
如果你认为它说turtle.GetX()将被调用n次并返回10,20,30,…,连续,三思!问题是,正如我们所说,期望是有粘性的。因此,第二次调用turtle.GetX()时,最后(最新)EXPECT_CALL()语句将匹配,并立即导致“违反上限”错误
using ::testing::Return;
for (int i = 1; i <= n; i++) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i));
}
正确做法如下:
正确的说法是turtle.GetX()将返回10,20,30,…就是明确地说期望是没有粘性的。换句话说,他们应该在饱和后立即退休:
using ::testing::Return;
for (int i = 1; i <= n; i++) {
EXPECT_CALL(turtle, GetX())
.WillOnce(Return(10*i))
.RetiresOnSaturation();
}
三、gMock Cookbook
1.Creating Mock Classes
class MyMock {
public:
MOCK_METHOD(ReturnType, MethodName, (Args...));
MOCK_METHOD(ReturnType, MethodName, (Args...), (Specs...));
};
前3个参数只是方法声明,分为3部分。第四个参数接受限定符的封闭列表,该限定符将影响生成的方法:
const -使被模拟的方法为const方法。如果需要重写const方法。
override -用override标记方法。如果覆盖,建议使用虚方法。
noexcept -用noexcept标记方法。如果重写noexcept方法。
Calltype(…)-设置方法的调用类型(例如:为STDMETHODCALLTYPE),在Windows中很有用。
ref(…)-用引用限定符标记方法指定。如果重写具有引用的方法则必须资格。例如ref(&)或ref(&&)。
class MockFoo {
public:
MOCK_METHOD(std::pair<bool, int>, GetPair, ()); // Won't compile!
MOCK_METHOD(bool, CheckMap, (std::map<int, double>, bool)); // Won't compile!
class MockFoo {
public:
MOCK_METHOD((std::pair<bool, int>), GetPair, ());
MOCK_METHOD(bool, CheckMap, ((std::map<int, double>), bool));
class MockFoo {
public:
using BoolAndInt = std::pair<bool, int>;
MOCK_METHOD(BoolAndInt, GetPair, ());
using MapIntDouble = std::map<int, double>;
MOCK_METHOD(bool, CheckMap, (MapIntDouble, bool));
};
2.Mocking Private or Protected Methods
你必须总是把一个模拟方法定义(MOCK_METHOD)放在模拟类的public:部分中,不管被模拟的方法是公共的,基类中的Protected或private。这允许ON_CALL和EXPECT_CALL从模拟类外部引用模拟函数。
(是的,c++允许子类改变基类中虚函数的访问级别。)
class Foo {
public:
virtual bool Transform(Gadget* g) = 0;
protected:
virtual void Resume();
private:
virtual int GetTimeOut();
class MockFoo : public Foo {
public:
MOCK_METHOD(bool, Transform, (Gadget* g), (override));
// The following must be in the public section, even though the
// methods are protected or private in the base class.
MOCK_METHOD(void, Resume, (), (override));
MOCK_METHOD(int, GetTimeOut, (), (override));
};
3.Mocking Overloaded Methods
class Foo {
// Must be virtual as we'll inherit from Foo.
virtual ~Foo();
// Overloaded on the types and/or numbers of arguments.
virtual int Add(Element x);
virtual int Add(int times, Element x);
// Overloaded on the const-ness of this object.
virtual Bar& GetBar();
virtual const Bar& GetBar() const;
class MockFoo : public Foo {
MOCK_METHOD(int, Add, (Element x), (override));
MOCK_METHOD(int, Add, (int times, Element x), (override));
MOCK_METHOD(Bar&, GetBar, (), (override));
MOCK_METHOD(const Bar&, GetBar, (), (const, override));
};
如果您没有模拟重载方法的所有版本,编译器将警告基类中的某些方法被隐藏。要解决这个问题,请使用using将它们引入范围:
class MockFoo : public Foo {
using Foo::Add;
MOCK_METHOD(int, Add, (Element x), (override));
// We don't want to mock int Add(int times, Element x);
};
4.Mocking Class Templates
template <typename Elem>
class StackInterface {
// Must be virtual as we'll inherit from StackInterface.
virtual ~StackInterface();
virtual int GetSize() const = 0;
virtual void Push(const Elem& x) = 0;
template <typename Elem>
class MockStack : public StackInterface<Elem> {
MOCK_METHOD(int, GetSize, (), (override));
MOCK_METHOD(void, Push, (const Elem& x), (override));
};
5.Mocking Non-virtual Methods
在这种情况下,模拟类不会与真实类共享一个公共基类,而是与真实类无关,但包含具有相同签名的方法。模拟非虚方法的语法与模拟虚拟方法(只是不添加override):
// A simple packet stream class. None of its members is virtual.
class ConcretePacketStream {
public:
void AppendPacket(Packet* new_packet);
const Packet* GetPacket(size_t packet_number) const;
size_t NumberOfPackets() const;
// A mock packet stream class. It inherits from no other, but defines
// GetPacket() and NumberOfPackets().
class MockPacketStream {
public:
MOCK_METHOD(const Packet*, GetPacket, (size_t packet_number), (const));
MOCK_METHOD(size_t, NumberOfPackets, (), (const));
};
一种方法是对需要使用包流的代码进行模板化。更具体地说,您将为您的代码提供一个数据包流类型的模板类型参数。在生产中,您将使用实例化模板ConcretePacketStream作为类型参数。在测试中,您将使用MockPacketStream实例化相同的模板。例如,你可以这样写:
template <class PacketStream>
void CreateConnection(PacketStream* stream) { ... }
template <class PacketStream>
class PacketReader {
public:
void ReadPackets(PacketStream* stream, size_t packet_num);
};
然后在生产代码中使用CreateConnection()和PacketReader,在生产代码中使用CreateConnection()和PacketReader测试。
MockPacketStream mock_stream;
EXPECT_CALL(mock_stream, ...)...;
.. set more expectations on mock_stream ...
PacketReader<MockPacketStream> reader(&mock_stream);
... exercise reader ...
依赖注入的eg:
-
ref:mock non-virtual methods
class A{
public:
int Funtion1(B& obj) {
//do something
std::string str = “mock non-virtual methods using templates”;
auto rst = obj.Function2(str);
//do something
class B{
public:
int Funtion2(std::string _str){ puts(_str.c_str()); }
}
当我们对类A的方法Function1进行UT防护的时候,不关心其中类B的方法Function2的执行结果,这时候该如何对其进行mock呢(Function2是非虚的)?
Gtest提供了依赖注入的方式
重构之后,类RefactorA变成了类模板,在实例化的时候把依赖的类B显式的“注入”进去,这时候进行UT的时候,就可以把“注入”的类B的方法Function2 进行mock,代码如下: