文章目录
前言
在进行单元测试时,我们想要测试自己缩写
函数A
,但是
函数A
却依赖于
函数B
,当
函数B
无法满足预期时就无法对
函数A
进行测试,主要由于下面几个原因:
函数B
依赖于硬件设备- 真实的
函数B
的返回值无法满足我们的预期 - 团队开发中
函数B
尚未实现
这时就需要对
函数B
进行打桩(仿真
mock
),使其达到我们预期的效果。
但是如同下面这种类型函数时,则需要深入下去,对基层的函数进行打桩:
typedefstruct{int num;char*ptr;char bot[20];} ST_OBJ;intfun_A(){fun_B();return0;}voidfun_B(ST_OBJ *ptObj){fun_C(ptObj);fun_D(ptObj);fun_E(ptObj);}
1. 下载网址
https://github.com/google/googletest
2. 为什么选择Google Mock
- 首先当然是我们遇到了在前言中所说的问题。
- 其次Goole的Mock也是代码,也是工程师写的,自己当然也可以去实现,但费时费力,难以避免出错,对于初学者很难。
- 再有就是Google Mock使用方便,只要
#include"gtest / gtest.h"和"gmock / gmock.h"
,你已经准备好了。 - 且功能强大。
3. C++简单的例子
Mock的功能强大,所以也有很多细节,但这些在后面再去了解,首先看一下如何利用它去写一个
TEST_F()
.
我们的代码中有一个这样的类:
classMachine{public:intIsSuccess(char*ptBotle);virtualboolTransform(Gadget* g)=0;protected:virtualvoidResume();private:virtualintGetTimeOut();};
总之就是有很多函数,现在我们想要去对这个类的函数进行
Mock
3.1 使用流程
- 声明一个Mock类继承自原始类
- 在
public
部分使用MOCK_METHOD
声明Mock
函数
classMockMachine:public Machine {public:/* MOCK_METHOD参数数量(函数名, 返回值类型(参数列表)) */MOCK_METHOD1(IsSuccess,int(char*ptBotle));MOCK_METHOD1(Transform,bool(Gadget* g));MOCK_METHOD0(Resume,void());MOCK_METHOD0(GetTimeOut,int());
到此就完成了,你并不需要去实现这些函数,函数的预期返回都可以在
TEST_F
中去指定,是我太小看
Mock
了,如果函数实现是固定的,那么在不同场景需要不同返回值时你还要去修改这个函数的实现吗?
- 在
TEST_F
中使用它
/* 步骤: */1. 导入GoogleMock中的一些命名空间, 方便后续指定自己Mock函数的期望
2. 创建Mock对象
3. 指定你对Mock函数的期望(一个函数应该被调用多少次,返回什么)
4. 打桩完成了,开始业务代码的测试函数, 并对其进行断言(ASSERT)
#include"path/to/mock-turtle.h"#include"gmock/gmock.h"#include"gtest/gtest.h"using::testing::AtLeast;// 1using::testing::Return;TEST(test_machine, ResumeTheMachine){
MockMachine OBJ_mach;// 2EXPECT_CALL(OBJ_mach,Resume())// 3.Times(AtLeast(1));ASSERT_TRUE(Fun_Resume(&OBJ_mach));// 4}
其中业务代码
Fun_Resume
的实现大概是这样:
boolFun_Resume(Machine *Ship){/*
...
*/
Ship->Resume();return TRUE;}
5. 指定自己的期望
如果定义的这个Mock供大家使用,那么每个人的需求都是不同的。即使一个人,在不同的单元测试用例中,对于该函数的期望也不可能不一样。
5.1 通用语法
使用
EXPECT_CALL
宏函数来定义.
它有两个参数:创建的Mock对象、方法及其参数
宏之后则是一些可选的配置
EXPECT_CALL(mock_object,method(matcher1, matcher2,...)).With(multi_argument_matcher)//多个参数的匹配方式指定.Times(cardinality)//执行多少次.InSequence(sequences)//函数执行顺序.After(expectations)//指定某个方法只能在另一个方法后执行.WillOnce(action)//一般使用return,指定一次调用的输出.WillRepeatedly(action)//一直输出.RetiresOnSaturation();//期待调用不会被相同的函数期待所覆盖/*--------------------------*/using::testing::Return;EXPECT_CALL(pencil,GetX(5)).Times(5).WillOnce(Return(100)).WillOnce(Return(150)).WillRepeatedly(Return(200));/*
pencil对象的GetX()方法将被调用五次,第一次返回`100`,第二次返回`150`,然后每次返回`200`.
并且函数`GetX()`指定入参是5.
*/
或者
TEST(mockcpp simple sample){MOCKER(function)/MOCK_METHOD(mocker, method).stubs()/defaults()/expects(once())[.before("some-mocker-id")][.with(eq(3))][.after("some-mocker-id")].will(returnValue(1))/.will(repeat(1,20))[.then(returnValue(2))][.id("some-mocker-id")]}
1、mock C函数用MOCKER;
mock 类成员方法先用MockObject mocker;声明一个mock对象,再用MOCK_METHOD(mocker, method)。
2、紧跟着MOCKER/MOCK_METHOD之后的是stubs、或者defaults、或者expects,三个必须有一个。(这是与AMOCK不同的地方,在这个层次上确定这三个关键字必须有一个,可以让mock语法更清晰)
stubs 表示指定函数的行为,不校验次数。
expects 与stubs的区别就是校验次数。(.expects(once()) / .expects(never()) / .expects(exactly(123)))
defaults 表示定义一个默认的mock规范,但它优先级很低;如果后面有stubs或者expects重新指定函数行为,就会按照新指定的来运行。
3、用will指定函数的返回值;
如果要指定20次调用都返回1,则用.will(repeat(1, 20));
要指定第一次返回1,第二次返回2,第三次返回3,就用
.will(returnValue(1))
.then(returnValue(2))
.then(returnValue(3))
如果你指定了前三次的返回值依次为1、2、3,那么第四次、第五次调用,都是返回最后的返回值3。
4、用id给一个mock规范指定一个名字,然后可以用after、before来指定多个mock应该的调用顺序。
注意before在with前,after在with后,id在整个mock规范的最后。
4.2 指定:调用次数
当一个函数不会被调用时,则应将其指定为0,这样貌似Mock也有一点断言的意思.
using::testing::_;EXPECT_CALL(foo,Bar(5)).Times(0);
4.3 指定:函数执行顺序
版权归原作者 Lzg_na 所有, 如有侵权,请联系我们删除。