Apache Weex 内嵌 Google Mock 设计文档解读:ACTION* 与 MATCHER* 宏的架构设计与实践
移动开发跨平台原生移动前端【免费下载链接】incubator-weexApache Weex (Incubating)项目地址https://gitcode.com/gh_mirrors/in/incubator-weex点击查看免费下载导读本文基于 Apache WeexIncubating仓库中随附的 Google Mock 设计文档 DesignDoc.md系统讲解 Google Mock 用于零样板自定义 Action桩行为与 Matcher参数匹配器的宏体系设计从ACTION(name)、ACTION_P(name, param)到ACTION_P9以及对应的MATCHER*宏族。读者将理解这些宏要解决的 C 测试痛点、其预定义符号与类型推断机制、参数化与重载规则、类型约束技巧以及它们在当前仓库 googlemock 中从设计提案到 gmock-generated-actions.h 与 gmock-generated-matchers.h 落地实现的全过程。Google Mock 是 Google 的 C 测试框架 README.md 明确说明的 C 单元测试配套方案被 Weex 的 C 核心weex_core测试体系随 googletest 一并引入用于为跨平台原生代码编写 mock 与断言。一、设计动机C 缺乏闭包导致的 Action 定义之痛设计文档开篇就点明核心问题由于 C 标准当时缺乏闭包closure机制在 Google Mock 中定义一个自定义 Action 需要付出不小的代价。假设你想实现递增 mock 函数第二个参数所指向的值并返回它传统方式需要先写一个普通函数再用Invoke包装int IncrementArg1(Unused, int* p, Unused) { return (*p); } ... WillOnce(Invoke(IncrementArg1));设计文档指出这种方式存在三个明显的不足冗余的占位参数即便该 Action 只关心 mock 函数的第二个参数定义时仍必须把其余参数全部列出用Unused占位非常繁琐参数个数被锁死这样定义的 Action 只能用在恰好有 3 个参数的 mock 函数上复用性差调用语法不自然使用者必须写Invoke(IncrementArg1)而不是更直观的IncrementArg1()。1.1MakePolymorphicAction()的救赎与代价文档进一步说明后两个问题可以通过MakePolymorphicAction()解决但代价是大量样板代码class IncrementArg1Action { public: template typename Result, typename ArgumentTuple Result Perform(const ArgumentTuple args) const { return (*tr1::get1(args)); } }; PolymorphicActionIncrementArg1Action IncrementArg1() { return MakePolymorphicAction(IncrementArg1Action()); } ... WillOnce(IncrementArg1());可以看到虽然现在可以像调用函数一样使用IncrementArg1()但使用者被迫手写一个模板类、实现Perform成员函数并手工解析参数元组tr1::get1(args)。设计文档给出的目标是让用户以 C 所能允许的最小样板代码量来定义自定义 Action。二、核心方案ACTION(name)宏设计文档提出引入一个新宏来消灭样板代码ACTION(name) { statements; }在命名空间作用域namespace scope中使用它就会定义一个名为name的 Action。宏体内部可以引用 mock 函数的第 K 个0 起始参数符号名为argK。前述递增第二个参数并返回的例子可以改写为ACTION(IncrementArg1) { return (*arg1); }之后即可直接使用... WillOnce(IncrementArg1());2.1 简洁是设计目标类型安全是底线设计文档强调使用ACTION宏时不需要指定 mock 函数参数的类型——简洁是首要设计目标。但这并不意味着失去类型安全如果*arg1不支持运算符编译器会报错如果(*arg1)的类型与 mock 函数返回类型不兼容编译器同样会报错。即类型由编译器推断类型错误在编译期暴露而非运行期。一个更综合的示例展示宏体内多语句能力ACTION(Foo) { (*arg2)(5); // 以 5 调用第 2 个参数函数指针 Blah(); // 调用普通函数 Blah() *arg1 0; // 把第 1 个参数指向的值设为 0 return arg0; // 返回第 0 个参数 }2.2 预定义符号表为方便与灵活ACTION宏体内部还提供了以下预定义符号设计文档原文表格符号含义argK_typemock 函数第 K 个0 起始参数的类型argsmock 函数的全部参数以元组tuple形式呈现args_typemock 函数全部参数组成的元组类型return_typemock 函数的返回类型function_typemock 函数的类型设计文档以一个桩 Action 示例int DoSomething(bool flag, int* ptr);给出了完整的符号绑定对照表预定义符号绑定值/类型arg0flag的值arg0_typebool类型arg1ptr的值arg1_typeint*类型args元组(flag, ptr)args_typestd::tr1::tuplebool, int*类型return_typeint类型function_typeint(bool, int*)类型注意args_type中出现的tr1::tuple是文档写作时的 C 标准库形态std::tr1当前仓库实现已演进为 C11 风格见下文源码实现印证。三、参数化 ActionACTION_P与ACTION_P2~ACTION_P9很多时候 Action 需要携带参数。设计文档提出第二个宏ACTION_P(name, param) { statements; }例如ACTION_P(Add, n) { return arg0 n; }即可写出// 返回参数 #0 5。 ... WillOnce(Add(5));3.1 两个容易混淆的术语设计文档在此处特意定义了术语避免混淆arguments实参调用 mock 函数时传入的值parameters参数实例化构造某个 Action 时传入的值。Add中的n是参数parameterarg0则来自 mock 函数的实参argument。与ACTION一样ACTION_P也无需声明参数类型——编译器会自动推断。若参数名为param还可以用 Google Mock 预定义的符号param_type引用其被推断出的类型。3.2 多参数版本为支持多参数 Action设计文档明确将提供ACTION_P2、ACTION_P3……以此类推。示例ACTION_P2(ReturnDistanceTo, x, y) { double dx arg0 - x; double dy arg1 - y; return sqrt(dx*dx dy*dy); }使用... WillOnce(ReturnDistanceTo(5.0, 26.5));文档还给出一个重要视角ACTION可以看作参数个数为 0 的参数化 Action 的退化形式——这为下面统一的类型命名规则埋下伏笔。四、高级用法4.1 按参数个数重载 Action可以轻松定义按参数个数重载的 ActionACTION_P(Plus, a) { ... } ACTION_P2(Plus, a, b) { ... }两个Plus宏分别生成不同后缀的类PlusActionP与PlusActionP2因此互不冲突。4.2 限制参数或实参的类型为了最大限度的简洁与可复用ACTION*宏族刻意不允许直接声明 mock 函数实参与 Action 参数的类型而把类型推断交给编译器。若确实需要显式约束类型设计文档给出几种技巧ACTION(Foo) { // 强制 arg0 可转换为 int int n arg0; ... use n instead of arg0 here ... } ACTION_P(Bar, param) { // 强制 arg1 的类型为 const char* ::testing::StaticAssertTypeEqconst char*, arg1_type(); // 强制 param 可转换为 bool bool flag param; }其中StaticAssertTypeEq是计划加入 Google Test 的编译期断言命名与 C0x 的static_assert对齐。4.3 Action 对象类型命名规则若你写的函数需要返回一个ACTION对象就必须知道它的类型。设计文档给出了简洁的命名规则与参数个数一一对应定义形式表达式对象类型ACTION(Foo)Foo()FooActionACTION_P(Bar, param)Bar(int_value)BarActionPintACTION_P2(Baz, p1, p2)Baz(bool_value, int_value)BazActionP2bool, int.........后缀规律Action0 参、ActionP1 参、ActionP22 参……必须为不同参数个数的 Action 挑选不同后缀否则无法按参数个数实现重载。五、什么时候该用什么时候不该用设计文档专门给出了使用建议章节提醒用户宏并非万能ACTION*宏非常方便但如果你需要大量复用某个 Action请同时考虑其他实现手段如ActionInterface或MakePolymorphicAction()其他方式虽然工作量大但能对 mock 函数实参与 Action 参数的类型施加更细粒度的控制通常能产生更好的编译器报错信息长期看收益更大它们还允许基于参数类型重载 Action而不仅仅基于参数个数。简言之宏换来了简洁也让出了类型控制力——这是一条明确的设计权衡。六、相关工作为什么不用 Boost Lambda 库设计文档敏锐地指出ACTION*宏实质上是在模仿闭包lambda 表达式 / 匿名函数两者目标都是降低定义函数时的语法开销。C0x 将原生支持 lambda但文档写作时 lambda 尚未进入 C 标准当时一些非标准库最典型的是BLL即 Boost Lambda Library试图缓解此问题但设计文档认为它们不适合用来定义 Action理由如下非标准且安装不普遍Google Mock 只依赖标准库与tr1::tuple属于新 C 标准、gcc 4 自带希望保持这一纯净依赖学习成本不低BLL 并不易学会被 C0x lambda 淘汰不愿让用户依赖一个行将消亡的库基于操作符、过于临时无法书写语句块也无法把 lambda 参数传给函数语义微妙、易迷惑新手例如表达式_1 foo中foo在表达式求值时只自增一次而_1在每次调用匿名函数时都会自增——远非直观。ACTION*宏族则完全规避了上述所有问题。七、未来改进方向设计文档在文末记录了三条演进设想体现了作者对 C 标准演进的预判组合ACTION*即在一个ACTION*内部调用另一个ACTION。作者并不确定是否需要因为把ACTION定义放进函数模板再组合函数模板可达到类似效果将根据用户反馈再定支持在函数体内使用ACTION*()当时 C 标准不允许用函数局部类型function-local types实例化模板因此ACTION*()只能出现在命名空间作用域。C0x 将解除此限制届时可重新审视实现支持 lambda 作为 ActionC0x 引入 lambda 后可能会支持直接以 lambda 充当 Action。仓库实现印证在 gmock-generated-matchers.h 的文件头注释中仍保留着MATCHER*()只能用于命名空间作用域原因是 C 尚不允许用函数局部类型实例化模板C0x 将修复此问题届时再考虑支持在函数内使用的说明——设计文档的这条限制原样延续到了实现代码里。八、姊妹方案MATCHER*宏的设计设计文档在 Action 宏之后规划了同样思路的匹配器宏MATCHER(name) { statements; }宏体内可用arg引用被匹配的值。例如MATCHER(IsPositive) { return arg 0; }之后IsPositive()即成为一个匹配器当且仅当被匹配值大于 0 时匹配成功。设计文档同时规划了MATCHER_P、MATCHER_P2……等参数化版本。九、源码实现印证从设计提案到落地宏设计文档是提案而当前仓库保留了该设计的最终实现可直接对照阅读验证每一条设计决策的落地形态。9.1 ACTION 宏族的实现在 gmock-generated-actions.h 中可以依次找到ACTION(name)生成name##Action类 内联工厂函数name()。内部定义嵌套类gmock_ImplF它继承::testing::ActionInterfaceF并提供function_type、return_type、args_type三个 typedef——正是设计文档第 2.2 节符号表的实现来源同时声明了gmock_PerformImpl参数列表从arg0_type一直到arg9_type支持最多 10 个实参ACTION_P(name, p0)生成模板类name##ActionPp0_type用p0_type承接被推断的参数类型类中保存成员p0并提供operator ::testing::ActionF()隐式转换ACTION_P2(name, p0, p1) 及 ACTION_P3、ACTION_P4、ACTION_P5、ACTION_P6、ACTION_P7、ACTION_P8、ACTION_P9逐级增加模板参数直到ACTION_P99 个参数与设计文档提供ACTION_P2、ACTION_P3等的规划完全一致。关键印证点宏生成的类名后缀Action/ActionP/ActionP2……与设计文档第 4.3 节的命名规则表格逐字吻合可见实现严格遵循了设计提案。9.2 MATCHER 宏族的实现在 gmock-generated-matchers.h 中MATCHER(name, description)生成name##Matcher类嵌套gmock_Implarg_type继承::testing::MatcherInterfaceGTEST_REFERENCE_TO_CONST_(arg_type)实现MatchAndExplain、DescribeTo、DescribeNegationTo三个虚函数MATCHER_P(name, p0, description)模板类name##MatcherPp0_type依次提供 MATCHER_P2 直到 MATCHER_P10最多 10 个参数。与设计文档的一处可见差异最终实现中每个 MATCHER 宏都额外带有一个description参数——在宏体返回空描述时实现会用FormatMatcherDescription根据名称与参数自动生成人类可读的匹配器描述用于失败信息输出。这正是设计提案在落地过程中被完善的典型例证也从侧面说明DescribeTo/DescribeNegationTo需要稳定可靠的文本来源。9.3 在 Weex 仓库中的定位当前仓库 googlemock 位于weex_core/test/third_party/googletest/下与 googletest 本体同属weex_coreC 测试基础设施的第三方依赖。在 googlemock/README.md 中Google Mock 的能力被概括为用简单宏轻松创建 mock 类、提供丰富的 matcher 与 action、支持无序/部分有序/完全有序的期望约束、且可由用户自行扩展——其中用宏轻松创建 mock与由用户扩展新 matcher 与 action两点正是本文所述ACTION*/MATCHER*宏设计所提供的核心能力。该 README 还建议新用户按Google Test 基础 → ForDummies → 构建说明的顺序入门而 DesignDoc.md 则面向希望理解宏机制内部设计的开发者。十、总结一份设计文档的完整生命周期回顾全文DesignDoc.md 完整走过了从问题定义 → 方案设计 → 细节规范 → 权衡说明 → 演进规划的设计闭环问题C 缺乏闭包自定义 Action 样板代码过多Invoke方案限制参数个数、语法不自然方案ACTION*宏族以最小样板定义 ActionargK/args/return_type等预定义符号与编译器类型推断保证简洁且类型安全扩展ACTION_P~ACTION_P9支持参数化按参数个数重载、param_type/StaticAssertTypeEq类型约束、统一的对象类型命名规则权衡宏简单但放弃类型控制重用途场景建议改用ActionInterface/MakePolymorphicAction()对照相对 BLL 等非标准库的五大优势以及基于 C0x 演进lambda、函数局部类型、组合的未来规划落地上述设计最终在 gmock-generated-actions.h 与 gmock-generated-matchers.h 中原样实现含MATCHER宏额外引入description参数这一实现期优化并为 Weex 的 C 核心测试所携带。对于任何希望深入 C 测试框架内部、理解如何用宏在无闭包语言中优雅模拟闭包的开发者这份文档与仓库内实现构成了一个完整的、可对照研读的案例。赞分享移动开发跨平台原生移动前端【免费下载链接】incubator-weexApache Weex (Incubating)项目地址https://gitcode.com/gh_mirrors/in/incubator-weex点击查看免费下载相关推荐基于 v8 内嵌 Google Mock 的 ACTION/MATCHER 自定义宏设计从 DesignDoc 到源码实现的全解基于 v8 内嵌 Google Mock 的 ACTION/MATCHER 自定义宏设计从 DesignDoc 到源码实现的全解 本文以 V8 引擎测试栈中内前端桌面应用MiniBlink49 内置 Google Mock ACTION* 自定义动作宏设计解析v8_6_7 测试栈中的 Action/Matcher 宏机制MiniBlink49 内置 Google Mock ACTION 自定义动作宏设计解析v8_6_7 测试栈中的 Action/Matcher 宏机制 本文以前端桌面应用miniblink49 中 v8_5_7 测试栈的 Google Mock 设计文档深度解读ACTION/MATCHER 宏如何在 C03 里造出lambdaminiblink49 中 v8_5_7 测试栈的 Google Mock 设计文档深度解读ACTION/MATCHER 宏如何在 C03 里造出lam前端桌面应用上一篇三分钟搞定B站缓存视频m4s转MP4的傻瓜式完整教程下一篇fumadocs-python 指南为 Fumadocs 一键生成 Python API 文档运行时内容源与构建期 MDX 转换创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

面试总挂?一文搞懂齐格勒原理,3个核心点让你秒懂

面试总挂?一文搞懂齐格勒原理,3个核心点让你秒懂

面试总挂?一文搞懂齐格勒原理,3个核心点让你秒懂 面试被问“齐格勒”原理,脑子一片空白?别慌。 很多刚入行的水利工程师,对着简历里的“掌握齐格勒理论”,面试官一深挖底层逻辑,立马哑火。 其实不用死记硬背公式。只要把 齐格勒…

2026/9/25 3:09:29 阅读更多 →
【合并多个RIS文件为一个文件】

【合并多个RIS文件为一个文件】

合并多个RIS文件为一个文件 from pathlib import PathSOURCE_DIR = Path(r"C:\Users\11\Desktop\test") OUTPUT_FILE = Path(r"C:\Users\11\Desktop\merged_ris_files.ris")def read_ris(path: Path) -

2026/9/25 3:59:37 阅读更多 →
3个步骤搞定点点通讯最佳实践,告别文档迷宫

3个步骤搞定点点通讯最佳实践,告别文档迷宫

3个步骤搞定点点通讯最佳实践,告别文档迷宫 官方文档动辄几百页,翻来翻去找不到核心逻辑,这是很多开发者在接触新框架时的共同噩梦。点点通讯(DiDi…

2026/9/25 6:09:38 阅读更多 →

最新新闻

MySQL表空间传输:从原理到实战,把大表迁移从小时级压缩到分钟级

MySQL表空间传输:从原理到实战,把大表迁移从小时级压缩到分钟级

老规矩,先给结论:MySQL自带的表空间传输(Transportable Tablespace)功能,是处理“单表或一批表快速换实例”最好用的手段之一,尤其在数据量已经上到几十GB、几百GB,mysqldump导出导入慢到让人抓…

2026/9/25 13:12:40 阅读更多 →
联合储能的配电网优化调度与新能源消纳能力评估研究

联合储能的配电网优化调度与新能源消纳能力评估研究

一个必须直面的现实:新能源装机冲上去之后,配电网为何最先“消化不良”这几年干配电网规划的人应该都有同样感受:分布式光伏、分散式风电、用户侧储能的接入申请像雪片一样涌过来,手头配电网的承载力评估还没做完,下一…

2026/9/25 13:12:40 阅读更多 →
NodeGui 中的 QMimeData 类详解:在拖放与剪贴板场景中传递 MIME 数据

NodeGui 中的 QMimeData 类详解:在拖放与剪贴板场景中传递 MIME 数据

桌面应用跨平台 【免费下载链接】nodegui A library for building cross-platform native desktop applications with Node.js and CSS 🚀. React NodeGui : https://react.nodegui.org and Vue NodeGui: https://vue.nodegui.org 项目地址: https://git…

2026/9/25 13:12:39 阅读更多 →
迅雷下载慢的根源排查:NAT类型、UPnP与连接数优化指南

迅雷下载慢的根源排查:NAT类型、UPnP与连接数优化指南

迅雷这类下载工具的速度问题,几乎每个用过的人都遇到过。同一个资源,有人跑满带宽,有人卡在几百KB,差距往往不在资源本身,而在几个容易被忽略的环节:网络地址转换(NAT)类型、UPnP端口…

2026/9/25 13:12:39 阅读更多 →
Django Ninja 查询参数(Query Parameters)完全指南:类型转换、默认值与 Schema 封装

Django Ninja 查询参数(Query Parameters)完全指南:类型转换、默认值与 Schema 封装

后端API设计 【免费下载链接】django-ninja 💨 Fast, Async-ready, Openapi, type hints based framework for building APIs 项目地址: https://gitcode.com/gh_mirrors/dj/django-ninja 点击查看 免费下载 本篇指南聚焦 Django Ninja 中 GET 查询参数…

2026/9/25 13:12:39 阅读更多 →
苏州品清装饰硬装服务怎么样,专业吗

苏州品清装饰硬装服务怎么样,专业吗

在苏州,一栋别墅往往承载着一个家庭半生的积蓄与期许。然而真正让业主辗转难眠的,常常不是选房那一刻,而是装修开始之后:效果图上美轮美奂的空间,落地后却面目全非;土建、硬装、园林、软装分属不同团队,出了…

2026/9/25 13:11:39 阅读更多 →

日新闻

AI元人文:从工具使用到思维重构的深度探索

AI元人文:从工具使用到思维重构的深度探索

最近半年我一直在琢磨一件事:AI元人文到底是什么?说白了,就是“用元视角重新审视人与AI的关系”,也在“探索AI如何反向逼着我们发现自己的思考边界”。标题里的“元探索”,在我看就是一层套一层的追问——当你用AI解决…

2026/9/25 0:00:41 阅读更多 →
Python+CNN车牌识别实战:从数据预处理到模型训练与部署

Python+CNN车牌识别实战:从数据预处理到模型训练与部署

简介:基于Python与卷积神经网络的车牌识别项目,面向计算机视觉初学者及智能交通开发者,目标是帮助用户掌握从数据预处理、模型构建到实际部署的完整流程。压缩包共25个文件,包含jpg/png图像样本、py训练脚本、md说明文档、dat数据…

2026/9/25 0:00:41 阅读更多 →
Vim基础操作全攻略:保存退出、模式切换与高频命令实战

Vim基础操作全攻略:保存退出、模式切换与高频命令实战

1. 项目概述1.1 核心需求解析今天聊聊Vim。写这个题目的原因是:几乎每个后端开发者、运维人员、数据工程师某天都会遇到一个场景——深夜加班,服务器登录界面只有黑底白字,编辑器只有vi/vim,你必须在五分钟内完成一次配置修改并保…

2026/9/25 0:00:41 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/24 14:34:13 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/25 11:15:26 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/24 14:33:56 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/24 12:50:34 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/24 14:33:48 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/24 12:49:17 阅读更多 →