如何解决非虚函数对象依赖
随着事物的接触越来越多,了解的越来越深入,我们总会发现一些新的问题或者不足。
就像前文提到的一样,我们在面对有对象的虚函数依赖的时候,可以使用
gmock
框架来为我们提供方便的模拟期望值,以便我们能撇除外界的影响(依赖)从逻辑上设计单元测试并持续的进行,但是并非所有对象的函数都设计成了虚函数,那么我们在面对依赖对象的非虚函数这个问题时,又该如何解决?
这个问题,已经有先行者遇到并且提出了解决方案:手动打桩、使用
hook
技术。
手动打桩有一个
stub
挺好用,只有一个头文件,包含进去就可以使用,但由于手动,所以使用起来相对有一些繁琐,并且不能很好的统计和校验调用次数。
使用
hook
技术的有
mockcpp
和
CppFreeMock
,这里使用的是
CppFreeMock
。因为它是基于
gmock
而来,是对
gmock
只能
mock
虚函数的一个补充,并且在用法上也能完美兼容
gmock
框架,如果单元测试已经是使用
gtest+gmock
的组合了,那么使用
CppFreeMock
的成本将不会高。
举个栗子:
void model::hardwardResponse(){memset(m_aRecvResponse,0, MAXLEN);int iRet = _pdevice->receivedatafromdevice(m_aRecvResponse);if(iRet ==0){
m_iHardwareErrCode = ERR_RECV_INVALID_LEN;return;}
m_iHardwareErrCode = m_aRecvResponse[ERRNO];if(m_iHardwareErrCode ==0){/* NO ERROR, response data process... */}}
这个函数,依赖于设备返回的数据。并且
receivedatafromdevice
函数是一个非虚函数:
classdevice{private:
string _serialno;
string _version;
string _firewareversion;
devicetype _type;
hardware* _phardware;public:device();~device();boolsenddatatodevice(unsignedchar* buff,int len);intreceivedatafromdevice(unsignedchar* receivedata);char*requestdeviceinfo(int requesttype);
devicetype requestdevicetype();};
我们不用修改原本已有的
modelTest
测试套件,需要先将
CppFreeMock
的头文件包含进来
#include"cpp_free_mock.h"
然后,开始设计我们的测试用例:
TEST_F(modelTest, hardwareResponse_Lenis0){/* 测试硬件返回长度为0的情况 */// 准备动作// 执行函数
pm->hardwardResponse();// 校验期望EXPECT_EQ(pm->getHardwardCode(),44444);}TEST_F(modelTest, hardwareResponse_Success){/* 测试硬件数据正常的情况 */// 准备动作// 执行函数
pm->hardwardResponse();// 校验期望EXPECT_EQ(pm->getHardwardCode(),0);EXPECT_EQ(memcmp(pm->getResponseData(), expectValues,254),0);}TEST_F(modelTest, hardwareResponse_Error){/* 测试硬件数据异常的情况 */// 准备动作// 执行函数
pm->hardwardResponse();// 校验期望EXPECT_EQ(pm->getHardwardCode(),4);}
根据上面
hardwardResponse
函数的实现,设计出上述三个单元测试用例,那么准备工作该如何使用
CppFreeMock
来设定预期呢?下面以第二个测试用例作详细说明:
// 准备动作unsignedchar expectValues[1024];for(int i =0; i <255; i++){
expectValues[i]=(i==4)?0:i;}auto mockerDevice =MOCKER(&device::receivedatafromdevice);EXPECT_CALL(*mockerDevice,MOCK_FUNCTION(_,_)).Times(1).WillOnce(DoAll(SetArrayArgument<1>(expectValues, expectValues+254),Return(32)));
auto mockerDevice = MOCKER(&device::receivedatafromdevice); // 创建
device
类的非虚成员函数
receivedatafromdevice
的
mock
对象
auto : 这里用的
auto
是
C++11
中的关键字
MOCKER :
MOCKER
宏是
CppFreeMock
中定义的,其作用是用于创建指定类的指定函数的
mock
对象。
普通成员函数用法:MOCKER(&类名::函数名) --例–>MOCKER(&device::receivedatafromdevice)
静态成员函数用法:MOCKER(类名::函数名) --例–>MOCKER(device::receivedatafromdevice)
普通全局函数用法:MOCKER(函数名) --例–>MOCKER(receivedatafromdevice)更多用法请查阅 CppFreeMock
EXPECT_CALL(*mockerDevice, MOCK_FUNCTION(
_
,
_
)).Times(1)
.WillOnce(DoAll(SetArrayArgument<1>(expectValues, expectValues+254), Return(32))); // 期望receivedatafromdevice
函数调用
1
次,传出的数据是
expectValues
数组中的前
255
个内容,并且返回接收数据长度为
32
个字节
MOCK_FUNCTION : 宏是
CppFreeMock
中定义的,表明是一个
mock
函数对象。
DoAll : 表明这次函数调用时,gmock
需要执行
DoAll(Action,Action)
中的多个
Action
SetArrayArgument(fisrtAddr, lastAddr) : 表示需要将从【
firstAddr
,
lastAddr
】 段中的数据传给第 n 个参数
其中
Times
、
WillOnce
、
Return
等的用法,[[5-如何解决虚函数对象依赖?|前文(函数有其他对象虚函数依赖如何单元测试)]] 有作说明,这里不多做赘述。
最前面的
expectValues
的数据定义及赋值,并且对错误码索引(
4
)特殊处理,赋值为
0
[无错误]
完善好测试用例之后,运行看看
CppFreeMock
是否能解决我们非虚函数依赖的问题:
测试用例成功通过,表明非虚函数
receivedatafromdevice
按照我们设定的预期执行。
对应的demo源码,请点击 mocknonvirtualfunc
也可扫码关注博主同名公众号"不解之榬",回复 “非虚” 获取
版权归原作者 不解之榬 所有, 如有侵权,请联系我们删除。