引言
前文我们已经讲解了gmock的基本语法,但是gmock只能mock虚函数,如果要mock非虚成员函数、静态成员函数、全局函数、重载函数、模板函数以及其他依赖库的函数时,gmock就很难实现。而cppfreemock可以支持这些函数的mock。(补充:新增了对虚函数的支持)
快速入门
1. mock样例
1.1 全局函数
// gloabal function
int g_func(int a, int b)
{
return a + b;
}
#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include "cpp_free_mock.h"
#include <string>
using namespace ::testing;
using namespace ::CppFreeMock;
namespace {
TEST(TestCppFreeMock, CaseGlobalFunction)
{
auto mock = MOCKER(g_func);
EXPECT_CALL(*mock, MOCK_FUNCTION(_, _))
.WillOnce(Return(1))
.WillRepeatedly(Return(2));
EXPECT_EQ(1, g_func(1, 2));
EXPECT_EQ(2, g_func(12, 2));
mock->RestoreToReal();
EXPECT_EQ(14, g_func(12, 2));
}
1.2 成员函数
class Adder {
public:
int add(int a, int b) const
{
return a + b;
}
};
TEST(TestCppFreeMock, CaseStaticMemberFunction)
{
auto mock = MOCKER(&Adder::add);
// 针对类的成员函数,要注意占位符会多出一个,即第一个为this指针
// 而全局函数或者静态成员函数占位符个数等于实际参数个数
EXPECT_CALL(*mock, MOCK_FUNCTION(_, _, _))
.WillRepeatedly(Return(2));
Adder adder;
EXPECT_EQ(2, adder.add(1, 2));
EXPECT_EQ(2, adder.add(12, 2));
mock->RestoreToReal();
EXPECT_EQ(14, g_func(12, 2));
}
1.3 静态成员函数
// static member function
class Printer {
public:
static std::string print(const std::string& str)
{
return str;
}
};
TEST(TestCppFreeMock, CaseStaticMemberFunction)
{
auto mock = MOCKER(Printer::print);
EXPECT_CALL(*mock, MOCK_FUNCTION(_))
.WillRepeatedly(Return(std::string("mocker")));
EXPECT_STREQ("mocker", Printer::print("hello").c_str());
mock->RestoreToReal();
EXPECT_STREQ("hello", Printer::print("hello").c_str());
}
1.4 函数重载
// overload function
class OverloadFunc {
public:
int foo() { return 0; }
int foo(int a) { return a;}
};
TEST(TestCppFreeMock, CaseOverloadFunction)
{
OverloadFunc overload_func;
typedef int (OverloadFunc::*FuncType0)();
typedef int (OverloadFunc::*FuncType1)(int);
auto mock0 = MOCKER((FuncType0)&OverloadFunc::foo);
EXPECT_CALL(*mock0, MOCK_FUNCTION(_))
.WillRepeatedly(Return(2));
EXPECT_EQ(2, overload_func.foo());
auto mock1 = MOCKER((FuncType1)&OverloadFunc::foo);
EXPECT_CALL(*mock1, MOCK_FUNCTION(_, _))
.WillRepeatedly(Return(2));
EXPECT_EQ(2, overload_func.foo(1));
mock0->RestoreToReal();
mock1->RestoreToReal();
EXPECT_EQ(2, overload_func.foo(1));
mock0->RestoreToReal();
mock1->RestoreToReal();
EXPECT_EQ(0, overload_func.foo());
EXPECT_EQ(1, overload_func.foo(1));
}
1.5 模板类成员函数
// template class
template <class T>
class AdderT {
public:
T add(T a, T b) { return a + b; }
T add(T a, T b, T c) { return a + b + c; }
T adder(T a) { return a; }
};
TEST(TestCppFreeMock, CaseTemplateFunction)
{
AdderT<int> adder;
// overload function
typedef int (AdderT<int>::*FuncType0)(int, int);
auto mock0 = MOCKER((FuncType0)&AdderT<int>::add);
EXPECT_CALL(*mock0, MOCK_FUNCTION(_, _, _))
.WillRepeatedly(Return(1));
EXPECT_EQ(1, adder.add(1, 2));
auto mock1 = MOCKER((FuncType1)&AdderT<int>::add);
EXPECT_CALL(*mock1, MOCK_FUNCTION(_, _, _, _))
.WillRepeatedly(Return(1));
EXPECT_EQ(1, adder.add(1, 2, 3));
mock0->RestoreToReal();
mock1->RestoreToReal();
EXPECT_EQ(3, adder.add(1, 2));
EXPECT_EQ(6, adder.add(1, 2, 3));
// normal function
auto mock2 = MOCKER(&AdderT<int>::adder);
EXPECT_CALL(*mock2, MOCK_FUNCTION(_, _))
.WillRepeatedly(Return(1));
EXPECT_EQ(1, adder.adder(2));
mock2->RestoreToReal();
EXPECT_EQ(2, adder.adder(2));
}
1.6 外部库函数
TEST(TestCppFreeMock, CaseOtherLibrary)
{
auto mock = MOCKER(std::atoi);
EXPECT_CALL(*mock, MOCK_FUNCTION(_))
.WillRepeatedly(Return(22867));
EXPECT_EQ(22867, std::atoi("123"));
mock->RestoreToReal();
EXPECT_EQ(123, std::atoi("123"));
}
2. 引入cppmockfree
git clone https://github.com/gzc9047/CppFreeMock.git
注意在引入cppfreemock之前请务必引入gtest和gmock。如何引入,在我之前的文章中均有提及。
3. makefile
CXX = g++
CXXFLAGS = -Wall
LIBES = -lgtest -lgtest_main -lpthread
LPATH = -L/tools/googletest/1.11.0/build/lib # 替换成自己lib路径
HPATH = -I/tools/googletest/1.11.0/googletest/include/ # 替换成自己的include路径
HPATH += -I/xxx/cpp_freemock/ #替换成自己的include路径
UTEST_OBJD = hello_unit_test
hello_unit_test:hello_unit_test.cpp
${CXX} -o $@ $+ -I ../ ${HPATH} ${CXXFLAGS} ${LIBES} ${LPATH}
clean:
rm -rf *_unit_test
4. 补充
4.1 支持虚函数mock
当前该框架不支持类的虚函数的mock,因此做了一些改进以支持对虚函数的mock。
class ClassA {
public:
virtual ~ClassA() {}
virtual int func(int num) { return num; }
};
TEST(TestCppFreeMock, CaseVirtualFunc)
{
ClassA a;
// typedef int (ClassA::*FuncT)(int); // 成员函数的函数类型
typedef int (*RealFuncT)(ClassA*, int); // 等价于成员函数的普通函数类型
// 单独声明一个变量用于避免pmf-conversions warnnig
auto func = &ClassA::func;
// 获取指向虚函数func的实际函数指针
// &ClassA::func获取的是虚函数func相对于虚函数表的偏移量+1的值
// 如此处func相对虚函数表的偏移值是8(64位系统),所以&ClassA::func的值是9
RealFuncT real_func = (RealFuncT)OBTAIN_REAL_FUNC(&a, (intptr_t&)func);
auto mock = MOCKER(real_func);
EXPECT_CALL(*mock, MOCK_FUNCTION(_, _))
.WillRepeatedly(Return(123456));
EXPECT_EQ(123456, a.func(123));
mock->RestoreToReal();
EXPECT_EQ(123, a.func(123));
}
其中OBTAIN_REAL_FUNC函数如下:
/// 在cpp_free_mock.h中添加如下宏:
#define OBTAIN_REAL_FUNC(obj_ptr, member_func) \
obtainRealFunc(obj_ptr, reinterpret_cast<const void*>(member_func)
/// 在cpp11/impl.h中添加如下函数:
inline void* obtainRealFunc(const void* obj_ptr, const void* member_func)
{
intptr_t func_offset = reinterpret_cast<intptr_t>(member_func);
intptr_t func_addr = *reinterpret_cast<intptr_t*>(*reinterpret_cast<const intptr_t*>(obj_ptr) + func_offset - 1);
return reinterpret_cast<void*>(func_addr);
}
上述获取虚函数地址的原理可以参考这篇文章:C++中如何获取虚表和虚函数的地址
总结
- CPPFreeMock能够支持几乎所有场景下的接口mock
- EXPECT_CALL的使用和gmock基本一致,更多的接口如:SaveArg、SetArgReferee等请参考gmock文档