1. Gmock 简介
Gmock是google开发的一套辅助测试的工具,它往往和GTest结合在一起使用。主要用于模拟其他模块约定的接口进行自测。即该接口形式已经定义,但实现并未给出,我们用Gmock模拟其内部实现,即设定方法运行形式,来进行测试。其作用就类似白盒测试中的打桩的概念。
测试某功能需要调用硬件或者其他服务,太过于复杂,例如测试对某函数对数据库的操作时,不需要真正调用数据库或者为了测试而搭建数据库,gmock可以将数据库接口打桩,模拟数据库的返回。
A模块调用B模块,目前测试A模块,但是B模块还没有实现,可以用gmock去B模块打桩,完成A模块对B模块的调用和测试。
- 测试某功能需要调用硬件或者其他服务,太过于复杂,例如测试对某函数对数据库的操作时,不需要真正调用数据库或者为了测试而搭建数据库,gmock可以将数据库接口打桩,模拟数据库的返回。
- A模块调用B模块,目前测试A模块,但是B模块还没有实现,可以用gmock去B模块打桩,完成A模块对B模块的调用和测试。
2. 编译googletest,生成静态库
我们要搭建gtest 测试工程,其实只需要将自己的测试单元程序与googletest的静态库链接起来。
在googletest 根工程根目录创建build文件夹,并进入build文件夹,可以使用明令行完成
mkdir build
cd build
接下来开始编译
cmake .. -G "MinGW Makefiles"
// 如果此前执行过cmake没有编译出想要的库,需要先删除CMakeCache.txt再重新执行
生成MakeFile之后,执行 make 命令
make
// 或者使用 cmake --build .
编译成功后,会在build文件夹下新生成一个lib文件夹,里面保存编译好的 libmock_main.a、libmock.a/ libgtest.a、libgtest_main.a。这四个静态库就是我们搭建自己的单元工程需要的。
3. 搭建单元测试工程
动手党可参考 windows从零搭建googletest之c++测试工程(CLion)
拿来党可下载 GitCode
4. Gmock 函数
三种mock模式
using ::testing::NiceMock;
using ::testing::NaggyMock;
using ::testing::StrictMock;
- NiceMock:NiceMock是一种宽松的Mock对象,它允许调用任何方法,而不会抛出异常。如果你不关心某些方法是否被调用,或者你只是想测试一些简单的场景,那么使用NiceMock是一个不错的选择。
- NaggyMock:NaggyMock是一种中等程度的Mock对象,它会在你没有设置期望的方法被调用时发出警告。这对于确保你的测试覆盖了所有需要测试的方法非常有用。
- StrictMock:StrictMock是一种严格的Mock对象,它会在你没有设置期望的方法被调用时抛出异常。这对于确保你的代码按照预期的方式运行非常有用,但是需要更多的设置和维护。
知道你也懒得看,那就用 NaggyMock 吧。
gMock典型应用流程:
- 引入用到的gMock名称
- 建立模拟对象
- 设置模拟对象默认动作
- 在模拟对象上设置预期
MOCK_METHOD
MOCK_METHOD(return_type, func_name, (args...), (specs));
举个栗子:
对于函数:
bool function(
char
* str1,
char
*str2) const override;
对应测试方法声明为:
MOCK_METHOD(bool, function, (char
*, char
*), (const override)
);
MOCK_METHODx
实现需要新写一个类继承于基类,基类包括将要测试的函数,包括纯虚函数等。
在类内我们定义测试接口的方法:
MOCK_METHODx(func_name,return_type(args...) );
x和函数的参数个数有关,func_name为函数名,return_type为函数func_name返回类型,args为参数表。
举个栗子:
对于函数:
bool function(
char
* str1,
char
*str2);
对应测试方法声明为:
MOCK_METHOD2(function,bool
(
char
* str1,
char
*str2));
EXPECT_CALL
构建类和方法后,我们可以通过调用
EXPECT_CALL(mock_object,
function
(matchers))// .With(...)
// .Times(...)
// .InSequence(...)
// .After(...)
// .WillOnce(...)
// .WillRepeatedly(...)
// .RetiresOnSaturation();
EXPECT_CALL 声明一个调用期待,function是mock_object对象中的mock方法,参数可通过matchers规则匹配。参数表的 _ 代表通配符,可以用任意参数代替进行控制。
_可替换为匹配器,单值匹配可以直接填入该值,详细的可调用自带匹配器,或手动编写。
方法 | 释义 |
With | 指定多个参数的匹配方式 |
Times | 设定方法调用的次数 |
InSequence | 指定函数执行的顺序 |
After | 指定某个方法只能在另一个方法之后执行 |
WillOnce | 执行一次方法时,将执行其参数的方法。一般我们使用Return方法,用于指定一次调用的输出。 |
WillRepeatedly | 表示一直调用一个方法时,将执行其参数的方法。 |
RetiresOnSaturation | 保证期待调用不会被相同的函数的期待所覆盖。 |
ON_CALL 和 EXPECT_CALL 区别
在 Google Mock 中,ON_CALL 和 EXPECT_CALL 都是用于设置模拟对象的行为的函数。 ON_CALL 用于设置一个模拟对象的预期行为,但不会检查该行为是否被调用。也就是说,如果设置了 ON_CALL,但是该行为没有被调用,测试也不会失败。
EXPECT_CALL 用于设置一个模拟对象的预期行为,并且会检查该行为是否被调用。如果设置了 EXPECT_CALL,但是该行为没有被调用,测试会失败。
因此,ON_CALL 适用于那些不是必须被调用的行为,而 EXPECT_CALL 适用于那些必须被调用的行为。
testing::Return
return方法,用于模拟被mock函数的返回值:
testing::Return(
true
)
testing::Return(
"123"
)
匹配器(Matcher)
作用:对一个对象进行验证匹配器可用在ON_CALL()及EXPECT_CALL()的matcher位置上,也可单独使用。
EXPECT_THAT(value, matcher)
ASSERT_THAT(value, matcher)
类型 | 关键字 | 用法说明 |
通用匹配 | 1. _ 2.A<type>()或者An<type>() | 1. 任意类型的参数都可以匹配 2.任何type类型的参数都可以匹配 |
一般匹配 | Ge(value) Gt(value) Le(value) Lt(value) Ne(value) IsNull()、NotNull() | 参数>=value 参数>value 参数<=vaule 参数<value 参数!=value 参数是空指针、参数是非空指针 |
浮点数匹配 | DoubleEq(double_value) FloatEq(float_value) | 参数= double_value 参数= float_value |
字符串匹配 | EndsWith(suffix) StartWith(prefix) HasSubstr(string) StrEq(string) StrNe(string) | 参数以后缀suffix结尾 参数以前缀prefix开始 参数包含string子字符 参数与string相同,大小写敏感 参数与string不同,大小写敏感 |
容器匹配 | ContainerEq(container) contains(element) Each(element) Sizels(m) IsEmpty | 参数和container有相同内容 包含满足e的元素,element为值 参数每个元素都满足element,element可以为值 参数的长度符合m的要求 参数为空容器 |
指针匹配 | Pointee(m) | 参数(指针)指向的内容符合m |
行为(Action)
gmock可选择使用系统或者用户自定义的行为
类型 | 关键字 | 用法说明 |
默认行为 | DoDefault() | 调用系统或者用户使用ON_CALL定义的默认行为 |
返回值 | Return() Return(value) ReturnNull() ReturnPointee(ptr) ReturnRef(variable) | 没有返回值的返回,适用于void函数 返回一个value值,必要时会做类型转换 返回一个空指针 返回指针ptr指向的值 返回variable的引用 |
副行为 | Assign(&variable, value) Throw(exception) | 将value赋给variable 抛出异常exception |
组合行为 | IgnoreResult(a) | 调用a但是忽略它的结果,a必须有返回值 |
次数生成器
次数生成器用于生成Times()中的次数参数
关键字 | 用法说明 |
AnyNumber() | 任意次数 |
AtLeast() | 期望最少n次 |
AtMost() | 期望最多n次 |
Between(m,n) | 期望介于m次和n次之间,包含m和n |
Exactly(n) | 期望n次 |
期望次序设置
Mock期望存在时时序关系,可指定满足的次序,行为有时序的期望链条
类型 | 说明 |
After | 指定期望被调用后才允许调用其他期望 |
InSequence对象 | InSequence对象的作用域范围内的期望必须已定义的顺序调用 |
Sequence对象 | 通过调用InSequence定义在同一个Sequence对象上的所有期望,必须以与定义时相同的顺序被调用 |
gmock 的约束
- 只能mock类的虚函数
- 对静态函数支持不友好
- 在调用mock函数前需设置好预期,否则其行为未定义
- 不支持多线程测试
因为静态函数是在类的命名空间中定义的,而不是在类的作用域中定义的,所以 gmock 无法直接模拟静态函数。但是,可以通过将静态函数包装在一个类中,然后使用 gmock 模拟该类来模拟静态函数。另外,也可以使用依赖注入等技术来避免对静态函数的直接依赖,从而使测试更加灵活和可维护。
// 被测对象
class MyClass {
public:
static int StaticFunction() {
// 静态函数的实现
}
};
// 模拟对象
class MockClass {
public:
MOCK_METHOD(int, StaticFunction, (), (const));
};
// 测试用例
TEST(MyClassTest, StaticFunctionTest) {
MockClass mock;
EXPECT_CALL(mock, StaticFunction())
.WillOnce(Return(42)); // 模拟静态函数的返回值为42
MyClass myObj;
int result = myObj.StaticFunction(); // 使用模拟函数替代静态函数
EXPECT_EQ(result, 42);
}