玩转单元测试之cppfreemock

引言

前文我们已经讲解了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文档
  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 24
    评论
评论 24
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值