gmock和cppfreemock原理学习

1.gmock用法

gmock(Google Mock)是 Google Test 的一个扩展库,专门用于 C++ 单元测试中的模拟(mocking) 。它的核心原理是通过 继承和方法重载/覆盖 来模拟 C++ 中的虚函数,从而在测试中隔离依赖对象,使测试更加可控。

gmock 通过 C++ 的多态机制(虚函数表 vtable) 来替换待模拟类的方法,使测试代码可以控制这些方法的行为。

cpp 复制代码
#include <gmock/gmock.h>

class ILogger {
public:
    virtual ~ILogger() = default;
    virtual void Log(const std::string& message) = 0;
};

class MockLogger : public ILogger {
public:
    // MOCK_METHOD(返回类型, 函数名, (参数列表), (限定符));
    // 1.负责定义虚函数的模拟版本,宏展开
    MOCK_METHOD(void, Log, (const std::string& message), (override));
};

// 2.定义mock类
MockLogger mock;
// 3.定义模拟函数期望行为
EXPECT_CALL(mock, Log("Hello")).Times(1);
mock.Log("Hello");  // 4.执行模拟函数,符合期望

MOCK_METHOD 实际上是一个 宏展开,它会生成:

  • 一个虚函数的声明 (覆盖基类)
  • 一个 gmock 代理方法,用于在运行时控制行为

展开的代码类似于:

cpp 复制代码
class MockLogger : public ILogger {
public:
    void Log(const std::string& message) override {
        // gmock 代理方法,用于检查调用
        gmock_Log(message);
    }

    // 由 gmock 生成的 "mock" 版本
    ::testing::MockFunction<void(const std::string&)> gmock_Log;
};

1.1 适用情况

(1)模拟依赖对象

  • 当被测代码依赖某个类,而这个类访问数据库、文件系统、网络等,无法直接测试时,可用 gmock 进行模拟。

(2)验证某个方法是否被正确调用

  • 例如:测试中 确保日志写入函数被调用 ,或者 确保某个 API 被正确触发。

2.EXPECT_CALL原理

EXPECT_CALL(mock, Method(args)) 主要用于:

  1. 指定期望的调用次数 (如 Times(1) 代表期望调用 1 次)。
  2. 检查参数是否匹配 (可以使用 Eq(5)、_(任意值)等)。
  3. 指定返回值或行为 (如 WillOnce(Return(10)) 表示返回 10)。

https://github.com/jwongzblog/myblog/blob/master/c++/单元测试Mock之c++-gmock实现原理.md

cpp 复制代码
// 定义了一个obj.gmock_func.InternalExpectedAt(...)去调用
#define GMOCK_EXPECT_CALL_IMPL_(obj, call) \
    ((obj).gmock_##call).InternalExpectedAt(__FILE__, __LINE__, #obj, #call)
#define EXPECT_CALL(obj, call) GMOCK_EXPECT_CALL_IMPL_(obj, call)


// 会添加一个新的expectations
typedef std::vector<internal::linked_ptr<ExpectationBase> >  UntypedExpectations;
untyped_expectations_.push_back(untyped_expectation);


// willOnce,会添加到,它是TypedExpectation的成员函数
// 当调用的时候会从对应模拟中返回结果并删除
typedef std::vector<const void*> UntypedActions;
untyped_actions_.push_back(new Action<F>(action));

// 其实还是不太明白,具体执行流程是怎么样的。怎么就能invoke了。

3.cppfreemock

gmock只能模拟虚函数,对业务可能有入侵。cppfreemock可以支持mock非虚成员函数、静态成员函数、全局函数、重载函数、模板函数以及其他依赖库的函数时。它的原理是函数指针hook。

玩转单元测试之cppfreemock-CSDN博客

cpp 复制代码
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));
}

3.1 函数指针hook

cpp 复制代码
#include <iostream>
#include <functional>

class Foo {
public:
    int Bar() { return 42; }  // 目标函数
};

// 函数指针 Hook(初始指向真实方法)
std::function<int(Foo*)> Bar_Hook = [](Foo* obj) { return obj->Bar(); };

// Mock 版本
int MockBar(Foo* obj) {
    return 99;
}

int main() {
    Foo foo;
    
    // 替换 Hook,指向 Mock 版本
    Bar_Hook = MockBar;
    
    // 通过 Hook 调用
    std::cout << "Result: " << Bar_Hook(&foo) << std::endl; // 输出 99

    return 0;
}
  • std::function<int(Foo*)> 作为 可变函数指针 ,用于调用 Foo::Bar。
  • 初始时,Bar_Hook 绑定到原始 Bar()。
  • 在测试时,替换 Bar_Hook 使其指向 MockBar(),这样所有调用都会执行 Mock 版本。

// 太牛了,还能这么搞,叹为观止。

函数指针 Hook 是一种通过修改函数指针的指向来改变函数行为 的技术。它常用于拦截函数调用 ,在不修改原代码的情况下 ,替换成自定义的行为(如 Mock、日志、监控等)。C++ 中的函数指针 本质上是存储函数地址的变量,可以动态修改。Hook 技术 通过 更改函数指针的值,让程序调用一个新的(Mock)函数,而不是原始函数。

hook普通函数:

cpp 复制代码
#include <iostream>
using namespace std;

// 原始函数
int OriginalFunction(int x) {
    return x * 2;
}

// 定义函数指针
int (*FunctionHook)(int) = OriginalFunction;

// Mock 版本
int MockFunction(int x) {
    return 100;  // 返回固定值
}

int main() {
    cout << "Before Hook: " << FunctionHook(10) << endl;  // 输出 20(调用原函数)

    // Hook:修改函数指针
    FunctionHook = MockFunction;

    cout << "After Hook: " << FunctionHook(10) << endl;  // 输出 100(调用 Mock 版本)

    return 0;
}

3.2 源码初探

cpp 复制代码
// MOCKER的宏定义
#define MOCKER_INTERNAL(function, identity)                                                        \
    ::CppFreeMock::MockerCreator::GetMocker<::CppFreeMock::TypeForUniqMocker<identity>>(function,  \
                                                                                        #function)

// GetMocker会够造一个对应的函数指针,并且存储到map中
static const std::shared_ptr<M> DoGetMocker(F function, const std::string& functionName) {
    const void* address = reinterpret_cast<const void*>((std::size_t&)function);
                    SimpleSingleton<RestoreFunctions>::getInstance().push_back(
                std::bind(MockerCacheType::RestoreCachedMockFunctionToReal));
            MockerCacheType::getInstance().insert(
                {{address, CreateMocker<I>(function, functionName)}});
}
相关推荐
kaixin_啊啊6 小时前
【用于更准确 G×E 基因组预测的大规模作物数据集与基于深度学习的多模态融合框架】论文学习
人工智能·深度学习·学习
xier_ran6 小时前
【infra之路】AI 编译器学习笔记(六):重拾 C++——lambda 与 MLIR 中的回调
c++·笔记·学习
别动我齐刘海8 小时前
Linux 系统调试学习2
linux·运维·c++·人工智能·学习·机器学习·机器人
m4Rk_9 小时前
【论文阅读】Agent 记忆机制(99):PrisMem——从整体分数搜索走向能力驱动的记忆自进化
论文阅读·人工智能·学习·开源·github
PC2005-cloud10 小时前
Mysql学习笔记:InnoDB Cluster 集群从零搭建实战,基于 Docker 实现高可用读写分离架构
笔记·学习·mysql
小陈phd10 小时前
深入理解agent学习笔记(七)——动态提示词与Skills
笔记·学习
我命由我1234510 小时前
田野笔记极简理解
经验分享·笔记·学习·职场和发展·求职招聘·职场发展·学习方法
一木 之林10 小时前
微信小程序开发全攻略·文章版|00 总览:74集学习路径与知识地图
人工智能·学习·计算机视觉·微信小程序·小程序
小陈phd11 小时前
深入理解agent学习笔记(六)——提示工程-优化系统提示词
笔记·学习
诸神缄默不语12 小时前
小春学习日刊 2026年第1期:2026.10.13
学习