一:Mock C code
对于c code,gcc 提供了编译链接选项:-Wl,-wrap
比如gcc编译时加入链接选项 -Wl,--wrap,malloc,
那么函数调用malloc的时候会调用自定义的__wrap_malloc,而原来的malloc会以__real_malloc进行调用。
所以程序需要定义一个__wrap_malloc,比如:
void* __wrap_malloc(size_t size) // 定义__wrap_malloc
{
printf("__wrap_malloc called, size:%zd\n", size); // log输出
return __real_malloc(size); // 通过__real_malloc调用真正的malloc
}
二:Mock C++ code (Google Mock/Gmock)
1) google mock介绍,以及EXPECT_CALL vs ON_CALL
https://slidedeck.io/DonaldWhyte/gmock-presentation
2) Mock函数设置函数参数
SetArgReferee<N>(value)
SetArgPointee<N>(value)
"Assign value to the variable referenced by the N-th (0-based) argument" and
"Assign value to the variable pointed by the N-th (0-based) argument" respectively.
比如:
ON_CALL(mock, Sign(Eq(0), _))
.WillByDefault(DoAll(SetArgPointee<1>("set argument1"), Return(0)));
3) using testing
using testing::_;
using testing::Return;
using testing::NiceMock;
using testing::SetArgPointee;
using testing::DoAll;
4) 示例
Gmock的前提是对于被Mock的函数所在的类,其基类提供的函数是虚函数,
这样Mock类可以继承该基类并提供Mock函数实现。比如基类:
class MyInterfaceBase
{
public:
virtual bool my_func(char* param1, int param2, bool *param3) = 0;
virtual bool my_func_variable_param(char* param1, bool param2 = false) = 0;
}
Mock类:
class My_Mock: public MyInterfaceBase
{
MOCK_METHOD3(my_func, bool(char* param1, int param2, bool *param3));
MOCK_METHOD2(my_func_variable_param_impl, bool(char* param1, bool param2));
bool my_func_variable_param(char* param1, bool param2 = false);
}
inline bool
My_Mock::my_func_variable_param(char* param1, bool param2)
{
return my_func_variable_param_impl(char* param1, bool param2);
}
UT测试:
注意,具体测试中需要有办法将待mock的类XXX 比如:
class XXX: public MyInterfaceBase
替换成My_Mock,比如XXX是其他某个类的成员变量 比如
class YYY
{
......
MyInterfaceBase xxx;
......
}
同时又提供了Set函数来set变量,那么可以用来替换成My_Mock.
My_Mock *mock_ptr = new NiceMock<My_Mock>();//NiceMock可以防止warning
ON_CALL(*mock_ptr, my_func(_, _, _)).WillByDefault(Return(true));
或者,如果想设置函数参数,比如将第三个参数bool *param3指向的值设置为true:
ON_CALL(*mock_ptr, my_func(_, _, _)).WillByDefault(
DoAll(SetArgPointee<2>(true), Return(true)));
5) 可变参数
gmock需要指定参数个数,比如MOCK_METHOD2固定2个函数参数,如果有可变参数,需要变通。
比如基类函数my_func_variable_param是可变参数,那么对于Mock类My_Mock来说,需要将
my_func_variable_param函数代理给一个新的固定参数函数,比如my_func_variable_param_impl,
然后通过这个代理函数实现ON_CALL/EXPECT_CALL设置函数返回值。
比如:
ON_CALL(*mock_ptr, my_func_variable_param_impl(_, _,)).WillByDefault(Return(true));
对于被测试函数,其调用my_func_variable_param可以传1个(另外一个采用默认参数)或者2个参数。
三: Mock global function
比如需要mock C/C++的global function, 可以参考下面的方式进行stub + mock
https://github.com/apriorit/gmock-global
https://github.com/apriorit/gmock-global-sample/blob/master/src/sample/main.cpp
https://www.codeproject.com/Tips/1262469/Gmock-Enhancements-Mocking-Global-Functions-and-Me
https://github.com/coolxv/cpp-stub
如果是mock C++ global function, 最好加上namespace, 以免在其他文件中也做同样的mock造成重复定义,示例:
假设待mock的函数原型为:
int My_func(int i, int j);
那么,mock写法为:
using namespace testing;
namespace testxxxxx
{
MOCK_GLOBAL_FUNC2(MOCK_My_func, int(int i, int j);
}
using namespace testxxxxx;
在具体的case里面进行stub替换:
EXPECT_GLOBAL_CALL(MOCK_My_func, MOCK_My_func(_,_)).WillRepeatedly(Return(0));
Stub stub;
stub.set(My_func, MOCK_My_func);
这里是具体的单元测试......
stub.reset(My_func);
一个特殊情况是针对C++的函数重载, 需要采用函数指针来解决:
比如重载的函数原型为:
int My_func(int i);
int My_func(int i, int j);
如果想mock两个参数的:
EXPECT_GLOBAL_CALL(MOCK_My_func, MOCK_My_func(_,_)).WillRepeatedly(Return(0));
int(*myFunc)(int i, int j) = &My_func;
Stub stub;
stub.set(myFunc, MOCK_My_func);
这里是具体的单元测试......
stub.reset(myFunc);
其他高级用法与gmock类似,比如:
EXPECT_GLOBAL_CALL(MOCK_My_func, MOCK_My_func(_,_)).WillRepeatedly(DoAll(SetArgReferee<1>(1), Return(0)));
参考:
https://stackoverflow.com/questions/32911356/google-mock-functions-changing-value-of-parameter
https://stackoverflow.com/questions/13933475/gmock-setting-default-actions-on-call-vs-expect-call