GoogleTest gMock(Google Mock)C++ 模拟框架实战指南:从 Mock 类编写到期望验证
【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/gh_mirrors/googl/googletest
gMock(Google Mock)是 GoogleTest 项目中专门用于编写和使用 C++ Mock 类的子框架,它帮助开发者以声明式语法定义模拟对象、控制其行为并自动验证调用期望。本指南以 googlemock/README.md 为骨架,结合 docs/gmock_for_dummies.md 教程与仓库源码,完整讲解 Mock 对象的定义、期望(expectation)的设置、匹配器(matcher)、基数(cardinality)与动作(action)的用法,读完后你将能够独立为任意 C++ 接口编写 Mock 类并在 googletest 中完成交互式测试。
gMock 是什么
gMock 是 Google 官方推出的 C++ Mock 框架,属于 GoogleTest(Google Testing and Mocking Framework)项目的一部分,与 googletest 同源、同步发布并遵循相同的要求。它专门用于解决 C++ 测试中的一个难题:如何验证你的模块与其他模块之间的交互是否正确。
gMock 的设计深受 Java/Python 生态中成熟 Mock 框架的启发,其灵感来源包括:
- jMock(Java 动态代理式 mock 框架)
- EasyMock(Java 经典 mock 框架)
- Hamcrest(通用匹配器断言库)
但 gMock 不是简单照搬,而是针对 C++ 语言特性专门设计,例如通过宏与模板在编译期生成 mock 实现,并充分兼容 C++ 的虚函数、重载、const 成员函数等机制。
gMock 的核心能力
根据仓库根目录下的 googlemock/README.md,gMock 提供以下核心能力:
- 声明式语法定义 Mock:用
MOCK_METHOD宏即可声明 mock 方法,无需手写实现; - 支持部分(混合)Mock:可以构造真实对象与 mock 行为交叉的混合对象,mock 掉部分方法、保留真实实现;
- 支持任意类型与重载函数:包括 const 方法、引用返回、模板参数等复杂签名;
- 内置丰富的匹配器:用于校验函数实参,如
Eq、Ge、_等; - 直观的行为控制语法:用
WillOnce、WillRepeatedly等子句描述方法被调用时的行为; - 自动验证期望:无需 record-and-replay 模式,调用违规会立即报错;
- 支持任意(部分)顺序约束:可以用
InSequence、After表达调用顺序; - 高度可扩展:用户可以自定义新的匹配器与动作;
- 不使用异常:全部基于 googletest 的断言机制报告失败;
- 易于学习和使用。
这些能力在 googlemock/include/gmock/gmock.h 中得到印证——该文件是用户唯一需要包含的主头文件,它聚合导出了 actions、cardinalities、function-mocker、matchers、more-actions、more-matchers、nice-strict、spec-builders 等全部子模块,并在文件头注释中给出了ON_CALL与EXPECT_CALL的完整语法骨架。
先厘清概念:Fake 与 Mock 不是一回事
在使用 gMock 之前,必须先区分两个在 TDD(测试驱动开发)社区中常被混淆的概念:
- Fake(假对象):有可工作的实现,但通常走捷径(比如为了降低成本),因此不适合生产环境。典型例子是内存文件系统——功能完整但数据不落盘。
- Mock(模拟对象):预先用"期望"编程的对象,这些期望构成了对它将要接收的调用的规格说明。
最核心的记忆点是:Mock 允许你检查自身与使用它的代码之间的交互。用 gMock 的流程分三步:
- 用简单的宏描述要 mock 的接口,宏会展开为 mock 类的实现;
- 创建 mock 对象,用直观的语法指定其期望与行为;
- 执行使用 mock 对象的代码,gMock 会在违规发生的瞬间捕获错误。
为什么需要 gMock:手写 Mock 的三大痛点
虽然 mock 对象能帮测试移除不必要的依赖、使其快速可靠,但在 C++ 中手动编写 mock 非常痛苦:
- 实现枯燥且易错:每个 mock 方法都要手写,人们宁可绕远路也不愿做;
- 质量不可控:手工 mock 质量参差不齐,常有各种临时限制;
- 经验不迁移:用过一个 mock 得到的经验无法复用到下一个。
相比之下,Java 和 Python 社区已有成熟框架(jMock、EasyMock 等)将 mock 创建自动化,mock 在这些社区中被证明是有效的实践。gMock 正是为 C++ 程序员补齐这一能力而生的。如果你遇到以下问题,gMock 就是你的答案:
- 受困于次优设计,想尽早做更多原型验证,而 C++ 原型开发太慢;
- 测试依赖太多库或昂贵资源(如数据库)而运行缓慢;
- 测试依赖网络等不可靠资源而变得脆弱;
- 想测试代码对失败(如文件校验和错误)的处理,但难以人为制造;
- 需要确认模块与其他模块的交互方式正确,但难以直接观察交互,只能笨拙地在动作结束后检查副作用;
- 想 mock 掉依赖,但依赖还没有现成的 mock 实现,又不满意手写的。
gMock 的双重价值:既是设计工具(让你早期反复试验接口设计,迭代越多设计越好),也是测试工具(切断测试的外部依赖、探测模块与协作者的交互)。
快速开始:gMock 随 googletest 一起分发
gMock 与 googletest 捆绑发布,不需要单独安装。在 CMake 工程中,可以通过根目录的 CMakeLists.txt 引入 gmock 与 gmock_main 目标;其自身的构建定义位于 googlemock/CMakeLists.txt,其中通过add_library生成gmock与gmock_main两个库目标,并通过target_include_directories暴露头文件路径。编译产物由 googlemock/src/gmock-all.cc(包含全部 gmock 源码)、googlemock/src/gmock_main.cc(提供main函数)等构成。
提示:
gmock_main自带main入口并自动调用InitGoogleMock(见 googlemock/include/gmock/gmock.h),它会解析 gmock 与 googletest 的命令行参数。如果使用gmock_main,测试中分配在堆上的 mock 对象会自动获得堆检查能力,从而保证析构时的期望最终验证得以执行。
实战案例:为"海龟绘图"接口编写 Mock
假设你在开发一个依赖 LOGO 风格绘图 API 的图形程序。直接运行程序与黄金截图比对的做法昂贵又脆弱(显卡升级导致抗锯齿变化就得更新所有黄金图)。正确的做法是利用依赖注入:把系统 API 包装成一个Turtle接口,让程序面向接口编程:
class Turtle { ... virtual ~Turtle() {} virtual void PenUp() = 0; virtual void PenDown() = 0; virtual void Forward(int distance) = 0; virtual void Turn(int degrees) = 0; virtual void GoTo(int x, int y) = 0; virtual int GetX() const = 0; virtual int GetY() const = 0; };注意:Turtle的析构函数必须是虚函数——所有打算被继承的类都应如此,否则通过基类指针 delete 对象时派生类析构函数不会被调用,导致内存泄漏等状态损坏。
PenUp()/PenDown()控制移动是否留下轨迹,Forward()/Turn()/GoTo()控制移动,GetX()/GetY()返回当前位置。生产代码使用真实实现,测试中则换成 mock 实现,从而轻松检查程序调用了哪些绘图原语、参数是什么、顺序如何——测试更健壮(不会因机器差异而失败)、更易读、运行快得多。
编写 Mock 类
如何定义:MOCK_METHOD 宏
按以下步骤定义MockTurtle:
- 从
Turtle派生MockTurtle; - 取一个
Turtle的虚函数(模板方式也能 mock 非虚方法,但复杂得多,参见 docs/gmock_cook_book.md); - 在子类的
public:区域写MOCK_METHOD();; - 把函数签名剪贴进宏:返回类型与方法名之间加一个逗号,方法名与参数列表之间再加一个逗号;
- mock const 方法时加第 4 个参数
(const)(括号必须); - 建议加
override关键字——const 方法第 4 个参数写成(const, override),非 const 方法写成(override)(非强制); - 重复直到所有要 mock 的虚函数完成(抽象类中所有纯虚方法必须被 mock 或 override)。
完成后效果如下:
#include <gmock/gmock.h> // 引入 gMock class MockTurtle : public Turtle { public: ... MOCK_METHOD(void, PenUp, (), (override)); MOCK_METHOD(void, PenDown, (), (override)); MOCK_METHOD(void, Forward, (int distance), (override)); MOCK_METHOD(void, Turn, (int degrees), (override)); MOCK_METHOD(void, GoTo, (int x, int y), (override)); MOCK_METHOD(int, GetX, (), (const, override)); MOCK_METHOD(int, GetY, (), (const, override)); };不需要在别处定义这些 mock 方法——MOCK_METHOD宏会自动生成定义。从源码看,该宏定义于 googlemock/include/gmock/gmock-function-mocker.h,内部通过GMOCK_PP_VARIADIC_CALL预处理变长参数并展开为GMOCK_INTERNAL_MOCK_METHOD_ARG_*模板实现,最终生成的FunctionMocker<F>负责记录调用、匹配期望与执行动作;同文件还保留了MOCK_METHOD0~MOCK_METHOD10、MOCK_CONST_METHOD0~MOCK_CONST_METHOD10等旧式宏(gmock-function-mocker.h)以保证向后兼容。
放在哪里:Mock 类的组织策略
定义 mock 类时要决定放置位置:
- 放在
_test.cc中:当被 mock 的接口Foo归自己团队所有时没问题;否则Foo一改,你的测试就可能崩。 - 一般原则:不要 mock 你不拥有的类。如果必须 mock 别人拥有的类,把 mock 类定义在
Foo所在的 Bazel 包(通常是同一目录或testing子目录)中,放在.h文件里,并构建为testonly=True的cc_library。这样所有人都能引用,Foo变化时只有一份MockFoo需要同步修改。 - 另一方案:在
Foo之上引入一层薄的FooAdaptor并面向新接口编程。因为FooAdaptor归你所有,能更从容地吸收Foo的变化;虽然初期工作更多,但精心设计适配器接口能让代码更易写易读。
在测试中使用 Mock
典型工作流
- 从
testing命名空间导入 gMock 名称(每个文件只需一次),即可不加限定符地使用; - 创建 mock 对象;
- 指定期望(方法被调用几次?带什么参数?应该做什么?);
- 执行使用 mock 的代码,可选地用 googletest 断言检查结果;若 mock 方法被多调用或参数错误,会立即报错;
- mock 析构时,gMock 自动检查其上所有期望是否已满足。
示例:
#include "path/to/mock-turtle.h" #include <gmock/gmock.h> #include <gtest/gtest.h> using ::testing::AtLeast; // #1 TEST(PainterTest, CanDrawSomething) { MockTurtle turtle; // #2 EXPECT_CALL(turtle, PenDown()) // #3 .Times(AtLeast(1)); Painter painter(&turtle); // #4 EXPECT_TRUE(painter.DrawCircle(0, 0, 10)); // #5 }如果painter没调用PenDown(),测试失败并输出如下信息:
path/to/my_test.cc:119: Failure Actual function call count doesn't match this expectation: Actually: never called; Expected: called at least once. Stack trace: ...两个实用提示:
- Tip 1:在 Emacs 缓冲区内运行测试时,可在失败行号上按
<Enter>直接跳转到失败的期望处; - Tip 2:若 mock 对象永不被删除,最终验证就不会发生。因此在堆上分配 mock 时建议开启堆检查器——使用
gtest_main库即可自动获得。
期望必须预先设置
重要规则:gMock 要求期望必须在 mock 函数被调用之前设置,否则行为是未定义的。不要在EXPECT_CALL()与 mock 函数调用之间交替进行,也不要在把 mock 传给某个 API 之后再设置期望。
因此EXPECT_CALL()应理解为"期望未来发生一次调用",而非"已经发生了调用"。原因在于:预先声明期望,gMock 才能在违规发生的第一时间(栈信息等上下文仍然可用的时刻)报告错误,调试会容易得多。
设置期望:掌握 EXPECT_CALL 语法
成功使用 mock 的关键是设置恰到好处的期望:太严,测试会被无关变更搞挂;太松,bug 会漏过去。gMock 提供了做到"恰到好处"所需的全部手段。
通用语法
EXPECT_CALL(mock_object, method(matchers)) .Times(cardinality) .WillOnce(action) .WillRepeatedly(action);宏有两个参数:先是 mock 对象,再是方法及其参数,两者用逗号(,)而非句点(.)分隔(这是出于技术原因的必要设计)。如果方法没有重载,还可以不带 matcher 调用:
EXPECT_CALL(mock_object, non-overloaded-method) .Times(cardinality) .WillOnce(action) .WillRepeatedly(action);这种省略参数列表的写法表示"接受任意参数",为免歧义只能用于非重载方法。
语法设计得读起来像英文。例如:
using ::testing::Return; ... EXPECT_CALL(turtle, GetX()) .Times(5) .WillOnce(Return(100)) .WillOnce(Return(150)) .WillRepeatedly(Return(200));意思是turtle的GetX()将被调用 5 次:第一次返回 100,第二次返回 150,之后每次返回 200。这种风格常被称为领域特定语言(DSL)。
为什么用宏实现?两个目的:一是让期望容易被识别(无论是
grep还是人眼),二是让 gMock 能在失败消息中包含期望所在的源文件位置,便于调试。
从源码看,EXPECT_CALL及配套子句定义于 googlemock/include/gmock/gmock-spec-builders.h,其完整语法还包括.With(multi-argument-matchers)、.InSequence(sequences)、.After(expectations)、.RetiresOnSaturation(),子句均可选,其中.InSequence()/.After()/.WillOnce()可出现任意多次。该头文件同时实现ON_CALL(mock_object, Method(...)).With(...).WillByDefault(...)用于指定 mock 方法的默认动作。
Matchers:期望什么样的参数
当 mock 函数带参数时,可以指定期望的参数:
// 期望海龟前进 100 个单位。 EXPECT_CALL(turtle, Forward(100));但经常不必如此精确——过度具体会让测试脆弱、掩盖意图。只指定必要的条件即可。对不关心的参数写_,表示"任意值都行":
using ::testing::_; ... // 期望海龟跳到 x=50 线上的某处。 EXPECT_CALL(turtle, GoTo(50, _));_是匹配器(matcher)的一个实例。匹配器像一个谓词,可检验实参是否符合预期,能用在EXPECT_CALL()中任何期望函数参数的位置。上面例子里的100和50其实也是匹配器,隐式等价于Eq(100)和Eq(50)——要求实参(用operator==)等于该值。常用类型的内置匹配器参见 docs/reference/matchers.md,自定义匹配器见 docs/gmock_cook_book.md:
using ::testing::Ge; ... // 期望海龟至少前进 100。 EXPECT_CALL(turtle, Forward(Ge(100)));若所有参数都不关心,可省略整个参数列表(仅限非重载方法):
// 期望海龟前进。 EXPECT_CALL(turtle, Forward); // 期望海龟跳到某处。 EXPECT_CALL(turtle, GoTo);若方法是重载的,则需要通过指定参数个数乃至参数类型来帮助 gMock 分辨期望哪个重载。
Cardinalities:将被调用多少次
EXPECT_CALL()后的第一个可选子句是Times(),其参数称为基数(cardinality),描述调用应发生的次数。基数可以像匹配器一样"模糊",从而精确表达测试意图。
特殊情形Times(0):表示该函数完全不应以给定参数被调用,一旦被(错误地)调用,gMock 就报告 googletest 失败。
内置基数的完整列表见 docs/gmock_cheat_sheet.md;从源码看,这些工厂函数在 googlemock/include/gmock/gmock-cardinalities.h 中实现,包括AtLeast(n)、AtMost(n)、AnyNumber()、Between(min, max)、Exactly(n)。其底层是一个拷贝式、不可变的Cardinality值对象,内部通过std::shared_ptr<const CardinalityInterface>持有实现(gmock-cardinalities.h),接口提供ConservativeLowerBound()/ConservativeUpperBound()、IsSatisfiedByCallCount()、IsSaturatedByCallCount()等判定方法;用户也可实现CardinalityInterface定义新基数。
Times()可省略。省略时 gMock 自动推断基数,规则如下:
- 若
EXPECT_CALL()中既无WillOnce()也无WillRepeatedly(),推断为Times(1); - 若有n个
WillOnce()且无WillRepeatedly()(n>= 1),基数为Times(n); - 若有n个
WillOnce()且有一个WillRepeatedly()(n>= 0),基数为Times(AtLeast(n))。
小测验:若函数期望被调用两次,实际却被调用四次,会发生什么?
Actions:应该做什么
mock 对象没有真实实现,用户必须告诉它在方法被调用时做什么。
首先,默认动作:mock 函数的返回类型是内建类型或指针时,它有默认动作——void函数直接返回,bool函数返回false,其他函数返回 0;C++11 及以上,返回类型可默认构造(即有默认构造函数)时,默认动作是返回一个默认构造的值。什么都不写就采用默认行为。
其次,当默认动作不适用时,用一系列WillOnce()后跟可选的WillRepeatedly()来指定每次匹配时的动作:
using ::testing::Return; ... EXPECT_CALL(turtle, GetX()) .WillOnce(Return(100)) .WillOnce(Return(200)) .WillOnce(Return(300));表示turtle.GetX()将被调用恰好三次(gMock 从三个WillOnce()推断出基数,因为我们没写Times()),分别返回 100、200、300。
using ::testing::Return; ... EXPECT_CALL(turtle, GetY()) .WillOnce(Return(100)) .WillOnce(Return(200)) .WillRepeatedly(Return(300));表示turtle.GetY()将被调用至少两次(两个WillOnce()加一个WillRepeatedly()而无显式Times()),前两次分别返回 100、200,第三次起返回 300。
若显式写了Times(),gMock 不再自行推断。当指定的次数超过WillOnce()的数量时,所有WillOnce()用尽后,每次执行默认动作(除非有WillRepeatedly())。
WillOnce()里除了Return()还能做什么?可以用ReturnRef(variable)返回引用,或调用预定义函数,更多动作见 docs/gmock_cook_book.md。
重要警告:EXPECT_CALL()语句对动作子句只求值一次,即使动作会被执行多次。注意副作用:
using ::testing::Return; ... int n = 100; EXPECT_CALL(turtle, GetX()) .Times(4) .WillRepeatedly(Return(n++));并不会依次返回 100、101、102……,而是每次都返回 100,因为n++只在EXPECT_CALL()执行时求值一次。同理Return(new Foo)会在执行EXPECT_CALL()时创建一次Foo对象,之后每次返回同一个指针。若希望副作用每次发生,需要定义自定义动作(见 docs/gmock_cook_book.md)。
再一个小测验:
using ::testing::Return; ... EXPECT_CALL(turtle, GetY()) .Times(4) .WillOnce(Return(100));turtle.GetY()被期望调用四次,但不会每次都返回 100——每次调用会消耗一个WillOnce(),之后执行默认动作。正确答案是:第一次返回 100,从第二次起返回 0(int函数的默认动作)。
多个期望:倒序匹配与覆盖规则
现实中往往对多个 mock 方法(甚至多个 mock 对象)设置期望。默认情况下,mock 方法被调用时,gMock 会按期望定义的相反顺序搜索,停在第一个参数匹配的活动期望上(可理解为"新规则覆盖旧规则")。若匹配的期望已无法再接受调用,会报上界违例失败:
using ::testing::_; ... EXPECT_CALL(turtle, Forward(_)); // #1 EXPECT_CALL(turtle, Forward(10)) // #2 .Times(2);若Forward(10)连续被调用三次,第三次报错——因为最后匹配的期望(#2)已饱和。但若第三次调用的是Forward(20),则没问题——此时 #1 匹配。
为什么倒序匹配?因为这允许用户在 mock 构造函数或测试夹具的 setup 阶段设置默认期望,然后在测试体内用更具体的期望来定制。所以同一方法的两个期望,更具体的匹配器应放在后面,否则会被后面的通用规则遮蔽。
实用技巧:常用
Times(AnyNumber())开一个兜底期望(非重载方法可省略参数,重载方法用_覆盖所有参数),使任何调用都在预期内。这对完全没有提及的方法("无趣"调用)不是必需的,但对已有部分期望、同时允许其他调用的方法很有用。参见 docs/gmock_cook_book.md 中 "Understanding Uninteresting vs Unexpected Calls" 一节。
顺序调用:InSequence 严格排序
默认情况下,期望不必按声明顺序满足。需要严格顺序时用InSequence:
using ::testing::InSequence; ... TEST(FooTest, DrawsLineSegment) { ... { InSequence seq; EXPECT_CALL(turtle, PenDown()); EXPECT_CALL(turtle, Forward(100)); EXPECT_CALL(turtle, PenUp()); } Foo(); }创建InSequence对象后,其作用域内的所有期望被放入序列,必须顺序发生。由于只依赖对象的构造与析构,名字无关紧要。此例验证Foo()按书写顺序调用三个函数,乱序即报错。
若只关心部分调用的相对顺序(任意偏序),gMock 也支持——细节见 docs/gmock_cook_book.md。相关的语法约束在 googlemock/test/gmock-spec-builders_test.cc 中有专门测试,例如InSequenceTest.AllExpectationInScopeAreInSequence验证作用域内全部期望入序列、ExpectCallSyntaxTest系列验证子句顺序约束(Times必须在InSequence之前等)。
期望默认是"粘性"的
小测验:如何测试海龟被要求回到原点恰好两次(忽略其他指令)?
using ::testing::_; using ::testing::AnyNumber; ... EXPECT_CALL(turtle, GoTo(_, _)) // #1 .Times(AnyNumber()); EXPECT_CALL(turtle, GoTo(0, 0)) // #2 .Times(2);假设turtle.GoTo(0, 0)被调用三次:第三次时 gMock 发现参数匹配期望 #2(总是选最后一个匹配的期望),而 #2 只允许两次调用,于是立即报错。
这个例子说明gMock 中的期望默认是"粘性"的——即使已达调用上界仍保持活动。这与许多其他 mock 框架不同,因为该规则让常见场景更容易表达和理解。
再验证一下理解:下面的代码是什么意思?
using ::testing::Return; ... for (int i = n; i > 0; i--) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)); }如果认为GetX()会被调用 n 次并依次返回 10、20、30……就错了!期望是粘性的,第二次调用GetX()时,最后一个(最新的)EXPECT_CALL()会匹配并立即触发"上界违例"错误——这段代码毫无用处。
正确的写法是显式声明期望不粘性(饱和即退休):
using ::testing::Return; ... for (int i = n; i > 0; i--) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)) .RetiresOnSaturation(); }还有更好的方式:既然这里调用顺序很重要,就用序列显式表达:
using ::testing::InSequence; using ::testing::Return; ... { InSequence s; for (int i = 1; i <= n; i++) { EXPECT_CALL(turtle, GetX()) .WillOnce(Return(10*i)) .RetiresOnSaturation(); } }另一种期望"不粘性"的情形:处于序列中的期望,当序列中排在其后的期望被使用后,它会自动退休(不再匹配任何调用)。
无趣调用(Uninteresting Calls)
mock 对象往往有许多方法,并非全部有趣。如果对某方法不感兴趣,什么也不用说。该方法被调用时,测试输出会出现警告但不会失败——这称为 "naggy"(唠叨)行为。要改变这种行为,见 docs/gmock_cook_book.md 中 "The Nice, the Strict, and the Naggy" 一节。
从源码看,gMock 默认是 naggy 的,googlemock/include/gmock/gmock-nice-strict.h 实现了三个模板类:
NiceMock<MockFoo>:允许无趣调用(静默);NaggyMock<MockFoo>:无趣调用时打印警告(当前默认行为,MockFoo与NaggyMock<MockFoo>表现相同);StrictMock<MockFoo>:把所有无趣调用视为错误。
实现上,NiceMockImpl/NaggyMockImpl/StrictMockImpl在构造时分别调用Mock::AllowUninterestingCalls/WarnUninterestingCalls/FailUninterestingCalls注册回调,析构时注销(gmock-nice-strict.h)。三者还"继承"基类构造函数,例如NiceMock<MockFoo>(5, "a")可用于构造带参的 nice mock。已知限制:NiceMock/NaggyMock/StrictMock只对在MockFoo类中直接用MOCK_METHOD*宏定义的方法生效,基类中定义的 mock 方法可能不受影响,且不支持嵌套使用。
深入源码:EXPECT_CALL 的底层实现
理解底层实现有助于正确使用 gMock。核心组件FunctionMocker<F>与UntypedFunctionMockerBase定义于 googlemock/include/gmock/gmock-spec-builders.h:
UntypedFunctionMockerBase是类型无关的基类,负责VerifyAndClearExpectationsLocked()(验证并清除期望)、报告无趣/意外调用消息、维护默认动作等;- 所有 mock 函数调用与期望操作通过静态互斥量
g_gmock_mutex串行化(gmock-spec-builders.h),保证多线程环境下 mock 对象状态的一致性——这正是"期望匹配采用倒序搜索"能够可靠实现的并发前提; TypedExpectation<F>是类型化的期望实现,持有 matcher、cardinality、action 序列与 retirement 状态。
因此,EXPECT_CALL(turtle, GetX()).Times(5).WillOnce(...)实际上是在构造一个TypedExpectation,将其注册进FunctionMocker的期望列表中;每次真实调用发生时,mocker 会加锁、倒序遍历期望、用 matcher 匹配实参、按 cardinality 判断是否可接受,再执行对应 action——违规(超调、参数不匹配、顺序错误)会立即转成 googletest 断言失败。
延伸阅读
gMock 的完整文档体系位于仓库的 docs 目录,可按需深入:
- docs/gmock_for_dummies.md:gMock 入门教程(本文主要依据);
- docs/gmock_cook_book.md:gMock 高级用法手册(自定义动作与匹配器、部分顺序、Nice/Strict/Naggy 等);
- docs/gmock_cheat_sheet.md:语法速查表(含基数列表);
- docs/gmock_faq.md:gMock 常见问题;
- docs/reference/matchers.md:内置匹配器完整参考;
- docs/reference/actions.md 与 docs/reference/mocking.md:动作与 mock 语法参考。
同时可阅读 googlemock/test/gmock-spec-builders_test.cc 等测试文件,观察EXPECT_CALL各子句在真实用例中的组合方式;全部测试由 googlemock/test/BUILD.bazel 组织,可用 Bazel 或 CMake 直接运行验证本文示例的行为。
【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/gh_mirrors/googl/googletest
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考