当前位置: 首页 > article >正文

gmock和cppfreemock原理学习

1.gmock用法

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

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

#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 代理方法,用于在运行时控制行为

展开的代码类似于:

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

// 定义了一个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博客

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

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

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

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

hook普通函数:

#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 源码初探

// 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)}});
}

相关文章:

gmock和cppfreemock原理学习

1.gmock用法 gmock&#xff08;Google Mock&#xff09;是 Google Test 的一个扩展库&#xff0c;专门用于 C 单元测试中的模拟&#xff08;mocking&#xff09;。它的核心原理是通过 继承和方法重载/覆盖 来模拟 C 中的虚函数&#xff0c;从而在测试中隔离依赖对象&#xff0…...

WSBDF レクチア 定义2 引理3 wsbdf的乘子

定义2 引理3 wsbdf的乘子 ここまで 寝みます❓...

AI日记app

一、需求分析与竞品调研 1. 核心功能需求 多媒体日记记录&#xff1a;支持语音、视频、图片的实时录制或上传。语音/视频转文字&#xff1a;自动将音频、视频内容转为可编辑的文字。文字编辑与排版&#xff1a;富文本编辑&#xff08;字体、颜色、标签&#xff09;、Markdown…...

单一职责原则(设计模式)

目录 问题&#xff1a; 定义&#xff1a; 解决&#xff1a; 方式 1&#xff1a;使用策略模式 示例&#xff1a;用户管理 方式 2&#xff1a;使用装饰者模式 示例&#xff1a;用户操作 方式 3&#xff1a;使用责任链模式 示例&#xff1a;用户操作链 总结 推荐 问题&a…...

Odoo免费开源CRM技术实战:从商机线索关联转化为售后工单的应用

文 / 开源智造 Odoo金牌服务 Odoo&#xff1a;功能强大且免费开源的CRM Odoo 引入了一种高效的客户支持管理方式&#xff0c;即将 CRM 线索转换为服务台工单。此功能确保销售和支持团队能够无缝协作&#xff0c;从而提升客户满意度并缩短问题解决时间。通过整合 CRM 模块与服…...

ChatGPT与DeepSeek:开源与闭源的AI模型之争

目录 一、模型架构与技术原理 二、性能能力与应用场景 三、用户体验与部署灵活性 四、成本与商业模式 五、未来展望与市场影响 六、总结 随着人工智能技术的飞速发展&#xff0c;ChatGPT和DeepSeek作为两大领先的AI语言模型&#xff0c;成为了行业内外关注的焦点。它们在…...

C语言(3)—循环、数组、函数的详解

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、函数二、循环与数组 1.循环2.数组 总结 前言 提示&#xff1a;以下是本篇文章正文内容&#xff0c;下面案例可供参考 一、函数 在C语言中&#xff0c;函数…...

架构师论文《论面向对象设计的应用与实现》

软考论文-系统架构设计师 摘要 我所在的公司是国内一家专注于智慧城市建设的科技企业&#xff0c;为适应城市数字化转型中对于高内聚、低耦合、可扩展性的技术需求&#xff0c;2021年3月&#xff0c;公司立项开发“智慧社区综合管理平台”&#xff0c;旨在整合物业管理、安防监…...

Deepseek Api Function Calling解析(tools、tool_calls)Deepseek函数调用流程图、Python代码示例

文章目录 Function Calling介绍**核心原理**1. **动态扩展模型能力**2. **JSON结构化交互** **实现步骤**&#xff08;以支持Function Calling的模型为例&#xff09;1. **定义可用函数**2. **模型匹配与生成**3. **开发者执行函数**4. **结果反馈给模型** **DeepSeek R1的当前…...

现代未来派品牌海报设计液体装饰英文字体安装包 Booster – Liquid Font

CS Booster – 具有动态流的液体显示字体 具有液体美感的现代显示字体 CS Booster 是一种未来主义的显示字体&#xff0c;采用流畅和有机的形式设计&#xff0c;赋予其流畅、灵活和不断移动的外观。独特的液体灵感形状和非刚性边缘使这款字体脱颖而出&#xff0c;提供一种既俏…...

python流水线自动化项目教程

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言1. 项目环境准备Python安装选择Python开发环境安装必要库 2. 数据获取与理解4. 模型训练流水线6. 模型保存7. 模型部署&#xff08;简单 Web 服务&#xff09;8…...

(十 四)趣学设计模式 之 策略模式!

目录 一、 啥是策略模式&#xff1f;二、 为什么要用策略模式&#xff1f;三、 策略模式的实现方式四、 策略模式的优缺点五、 策略模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&#xff0c;可以多多支…...

EMO模型详解及代码复现

EMO定义 EMO(Efficient Mobile Networks)是一种 面向移动端的轻量化网络模型 ,旨在 在参数、FLOPs和性能之间实现平衡 ,特别适用于 密集预测任务 。EMO的设计理念源于对CNN和Transformer架构的深入理解,通过整合两者的优势,实现了高效的模型性能。 EMO的核心是 反向残差…...

kkfileview部署

kkfileview部署 链接: 官方文档 链接: gitee 链接: github 首先打开官网如下&#xff1a; OK&#xff0c;我们从官方文档的教程中看到&#xff0c;部署步骤如下: 是不是很简单&#xff0c;没错&#xff0c;于是我们按照步骤从码云上下载&#xff0c;然后解压&#xff0c;然…...

leetcode_34 在排序数组中查找元素的第一个和最后一个位置

1. 题意 给定一个非递减的数组&#xff0c;找出给定元素的开始位置和 结束位置。 2. 题解 题目要求复杂度为 log ⁡ ( n ) \log (n) log(n), 因此不能用双指针了。 这个题目练习二分挺好的。 2.1 双指针 还是把双指针的写下来吧。 class Solution { public:vector<i…...

网络编程——UDP

UDP编程使用套接字&#xff08;Socket&#xff09;进行通信。下面是基于UDP协议进行网络编程的基本步骤。 1. 创建套接字 首先&#xff0c;客户端和服务器都需要通过 socket() 系统调用创建一个UDP套接字。 2. 配置地址和端口 UDP是无连接的&#xff0c;因此你不需要像TCP一…...

文件描述符(File Descriptor)

一、介绍 内核&#xff08;kernel&#xff09;利用文件描述符&#xff08;file descriptor&#xff09;来访问文件。文件描述符是非负整数。打开现存文件或新建文件时&#xff0c;内核会返回一个文件描述符。读写文件也需要使用文件描述符来指定待读写的文件。 二、功能 文件…...

【3天快速入门WPF】12-MVVM

目录 1. 什么是MVVM2. 实现简单MVVM2.1. Part 12.2. Part 21. 什么是MVVM MVVM 是 Model-View-ViewModel 的缩写,是一种用于构建用户界面的设计模式,是一种简化用户界面的事件驱动编程方式。 MVVM 的目标是实现用户界面和业务逻辑之间的彻底分离,以便更好地管理和维护应用…...

钉钉MAKE AI生态大会思考

1. 核心特性 1.1 底层模型开放 除原有模型通义千问外,新接入猎户星空、智普、MinMax、月之暗面、百川智能、零一万物。 1.2 AI搜索 AI搜索贯通企业和个人散落在各地的知识(聊天记录、文档、会议、日程、知识库、项目等),通过大模型对知识逻辑化,直接生成搜索的答案,并…...

[操作系统] 文件的软链接和硬链接

文章目录 引言硬链接&#xff08;Hard Link&#xff09;什么是硬链接&#xff1f;硬链接的特性硬链接的用途 软链接&#xff08;Symbolic Link&#xff09;什么是软链接&#xff1f;软链接的特性软链接的用途 软硬链接对比文件的时间戳实际应用示例使用硬链接节省备份空间用软链…...

【TI毫米波雷达】DCA1000的ADC原始数据C语言解析及FMCW的Python解析2D-FFT图像

【TI毫米波雷达】DCA1000的ADC原始数据C语言解析及FMCW的Python解析2D-FFT图像 文章目录 ADC原始数据C语言解析Python的2D-FFT图像附录&#xff1a;结构框架雷达基本原理叙述雷达天线排列位置芯片框架Demo工程功能CCS工程导入工程叙述Software TasksData PathOutput informati…...

基于ai技术的视频生成工具

一、通用型AI视频生成工具 腾讯智影 特点&#xff1a;支持数字人播报、文字转视频&#xff0c;提供免费模板和素材库&#xff0c;登录即送5分钟免费时长&#xff0c;每日签到可兑换额外额度。 限制&#xff1a;免费版分辨率较低&#xff0c;部分高级功能需付费。 LunaAI.vid…...

Python Cookbook-2.29 带版本号的文件名

任务 如果你想在改写某文件之前对其做个备份&#xff0c;可以在老文件的名字后面根据惯例加上三个数字的版本号。 解决方案 我们需要编写一个函数来完成备份工作: def VersionFile(file_spec, vtypecopy):import os,shutilif os.path.isfile(file_spec):#检查vtype参数if v…...

阿里云轻量级服务器通过宝塔安装PgVector要点

设置环境变量&#xff1a; export PG_HOME/www/server/pgsql export LD_LIBRARY_PATH$LD_LIBRARY_PATH:/www/server/pgsql/lib export PG_CONFIG/www/server/pgsql/bin/pg_config export PGDATA/www/server/pgsql/data PATH$PATH:$HOME/.local/bin:$HOME/bin:$PG_HOME/bin ali…...

LeeCode题库第三十九题

39.组合总和 项目场景&#xff1a; 给你一个 无重复元素 的整数数组 candidates 和一个目标整数 target &#xff0c;找出 candidates 中可以使数字和为目标数 target 的 所有 不同组合 &#xff0c;并以列表形式返回。你可以按 任意顺序 返回这些组合。 candidates 中的 同…...

Android Activity栈关系解析

在 Android 系统中&#xff0c;这些类共同构成了 Activity 任务栈管理的核心架构。它们的关系可以类比为一栋大楼的管理体系&#xff0c;每个类负责不同层级的任务。以下是它们的详细解释和实际场景示例&#xff1a; 1. ActivityRecord&#xff08;活动记录&#xff09; 是什么…...

B/B+树与mysql索引

数据结构操作网站&#xff1a;https://www.cs.usfca.edu/~galles/visualization/Algorithms.html B树 算法平均最差空间O(n)O(n)搜索O(log n)O(log n)插入O(log n)O(log n)删除O(log n)O(log n) B树 算法平均最差空间O(n)O(n)搜索O(log n)O(log n)插入O(log n)O(log n)删除O(…...

1.2.3 使用Spring Initializr方式构建Spring Boot项目

本实战概述介绍了如何使用Spring Initializr创建Spring Boot项目&#xff0c;并进行基本配置。首先&#xff0c;通过Spring Initializr生成项目骨架&#xff0c;然后创建控制器HelloController&#xff0c;定义处理GET请求的方法hello&#xff0c;返回HTML字符串。接着&#xf…...

【踩坑随笔】`npm list axios echarts`查看npm依赖包报错

npm list axios echarts查看npm依赖包出现以下报错&#xff0c;原因就是包的版本匹配问题&#xff0c;按照提示降axios版本或者自己升找合适的got版本&#xff0c;我这里是选择了降版本。本文记录仅做解决思路参考不一定适配大家的实际情况。 weed-detection-system1.0.0 E:\P…...

十四届蓝桥杯JAVA-b组-合并石子

点我写题 思路&#xff1a;区间dp和缝合dp板子题&#xff0c;先用个dp[i][j][k]表示考虑区间[i,j]合并成颜色k的最小代价&#xff0c;然后用min[i][j]存一下[i,j]区间合并的最小代价&#xff0c;即min(dp[i][j][0-2])&#xff0c;has[i][j]表示区间[i,j]是否能合并&#xff0c…...