GoogleMock Matchers与Actions实战:10个必会技巧精通C++模拟测试
【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/GitHub_Trending/go/googletest
GoogleMock 是 GoogleTest 官方提供的 C++ 模拟测试(Mock)框架,而Matchers(匹配器)与Actions(动作)正是 GoogleMock 最核心的两大武器:Matcher 决定"哪些调用我感兴趣",Action 决定"被调用后我该做什么"。这篇实战教程将用 10 个技巧带你快速掌握 GoogleMock Matchers 与 Actions,让你的 C++ 单元测试又快又稳。
技巧1:用内置 Matcher 精确描述参数条件
Matcher 是 GoogleMock 用来匹配函数参数的"条件表达式"。最常用的就是比较类匹配器:
using ::testing::Ge; using ::testing::NotNull; EXPECT_CALL(foo, DoThis(Ge(5))) // 参数必须 >= 5 .WillOnce(Return('a')); EXPECT_CALL(foo, DoThat("Hello", NotNull())); // 第二个参数不能为空📌 常用内置 Matcher 一览(完整清单见 reference/matchers.md):
| Matcher | 含义 |
|---|---|
Eq(v)/ 直接写v | 参数等于 v |
Lt/Gt/Le/Ge/Ne | 小于 / 大于 / 小于等于 / 大于等于 / 不等于 |
IsNull()/NotNull() | 空指针 / 非空指针 |
HasSubstr(s) | 字符串包含子串 s |
ContainsRegex(s) | 字符串匹配正则表达式 |
技巧2:善用通配符_和An<type>()
不是每个参数都需要精确校验。_匹配"任何值",是写测试时最省心的符号:
using ::testing::_; using ::testing::An; EXPECT_CALL(foo, DoThat(_, NotNull())); // 第一个参数随便传 EXPECT_CALL(printer, Print(An<int>())); // 匹配 int 类型的任何值💡 新手建议:只匹配你真正关心的参数,其余用_占位,测试代码会清爽很多。
技巧3:用 AllOf / AnyOf / Not 组合出复杂条件
单个条件不够用时,GoogleMock 提供逻辑组合器,像搭积木一样拼出复杂 Matcher:
using ::testing::AllOf; using ::testing::AnyOf; using ::testing::Not; using ::testing::HasSubstr; // 参数必须 > 5 且 != 10 EXPECT_CALL(foo, DoThis(AllOf(Gt(5), Ne(10)))); // 第一个参数不能包含子串 "blah" EXPECT_CALL(foo, DoThat(Not(HasSubstr("blah")), nullptr));AllOf(...):所有条件都要满足(AND)AnyOf(...):满足任一条件即可(OR)Not(m):条件取反
更多组合技巧(如把 Matcher 当断言用、SafeMatcherCast类型转换)可查阅官方菜谱 docs/gmock_cook_book.md 的 "Using Matchers" 章节。
技巧4:类型敏感?用TypedEq消除重载歧义
Matcher 是静态强类型的——这既是优点也是坑:用错类型会直接编译报错。当被 mock 的方法存在同名重载时,编译器需要你明确指出类型:
using ::testing::TypedEq; using ::testing::An; EXPECT_CALL(printer, Print(An<int>())); // 匹配 Print(int) EXPECT_CALL(printer, Print(TypedEq<char>('a'))); // 匹配 Print(char)如果手上有一个Matcher<Base*>却需要匹配Derived*,可以用SafeMatcherCast<T>(m)做安全转换(规则详见 cookbook 的 Casting Matchers 一节)。
技巧5:用 MATCHER_P 自定义你的专属 Matcher
内置 Matcher 不够用?三行代码就能定义自己的。以下测试代码就是官方对MATCHER_P的实战验证(gmock-matchers-misc_test.cc):
// 定义一个参数化 Matcher:判断 arg 是否大于 32 且大于参数 n MATCHER_P(IsGreaterThan32And, n, "is > 32 and > " + Show(n)) { return arg > 32 && arg > n; } EXPECT_THAT(40, IsGreaterThan32And(25)); // 使用MATCHER_P(名字, 参数, 描述):带 1 个参数MATCHER_P2(名字, 参数1, 参数2, 描述):带 2 个参数- 体内部用
arg引用被匹配的值,arg0/arg1对应参数
这样你的断言会变得像自然语言一样可读,团队新人一看就懂。
技巧6:Return 家族——返回值三兄弟
Action 决定 mock 方法被调用时"做什么"。返回值场景下有三个易混淆的 API:
| Action | 适用场景 |
|---|---|
Return(v) | 返回 v 的副本(设定期就拷贝,之后 v 怎么变都不影响) |
ReturnRef(v) | 返回 v 的引用,仅当 mock 方法返回类型是引用时使用 |
ReturnPointee(&v) | 每次调用时返回 v 的当前值(live 值) |
⚠️ 新手最常踩的坑:Return()在设置期望时就把值拷走了。想让 mock 方法"实时"读到变量的新值,请用ReturnPointee(&x)而不是std::ref(x)。完整对比见 cookbook 的 Returning Live Values 一节。
技巧7:DoAll 一次调用执行多个动作
当 mock 方法既要产生副作用、又要返回值时,DoAll让你按顺序执行一串动作,以最后一个动作的返回值作为方法返回值:
using ::testing::DoAll; using ::testing::Return; EXPECT_CALL(foo, Bar(_)) .WillOnce(DoAll(action_1, // 比如记录日志 action_2, // 比如修改全局状态 Return(true))); // 最后一个动作必须提供返回值💡 注意:前 n-1 个动作应返回void,否则编译不通过。
技巧8:SaveArg 系列——把参数存下来慢慢验证
当参数匹配条件很复杂时(比如整个数组内容、整个 Protobuf),直接在EXPECT_CALL里堆 Matcher 会让失败信息难以阅读。更清晰的做法:先用动作把参数"存起来",测试运行完再逐项断言:
using ::testing::SaveArg; using ::testing::SaveArgPointee; EXPECT_CALL(foo, SendValues) .WillOnce(DoAll(SaveArg<1>(&actual_array), // 存第1个参数 SaveArgPointee<2>(&actual_proto))); // 存第2个参数指向的值 ... // 运行被测代码 EXPECT_THAT(actual_array, ElementsAre(1, 4, 4, 7));相关 Action 家族还有SetArgPointee<N>(v)(修改第 N 个参数指向的变量)、SetArgReferee<N>(v)(修改第 N 个引用参数),是模拟"输出参数"行为的利器。
技巧9:Lambda / Invoke——让任意可调用对象变身 Action
任何函数、lambda、仿函数都可以直接作为 Action 使用,这是最灵活的兜底方案:
using ::testing::Invoke; // 直接传 lambda(C++11 起最常用) EXPECT_CALL(foo, GetValue()) .WillOnce([] { return 42; }); // Invoke 调用全局函数,未用的参数声明为 Unused 即可 double Distance(Unused, double x, double y) { return sqrt(x*x + y*y); } EXPECT_CALL(mock, Foo("Hi", _, _)).WillOnce(Invoke(Distance));📌 所有可用 Action 的完整清单(含Throw、ReturnRoundRobin、WithArg等)见官方参考 reference/actions.md。
技巧10:ON_CALL 设置默认动作,减少重复样板
EXPECT_CALL表示"一定会被调用",而ON_CALL表示"默认行为"——当你不想对某个方法做调用次数约束、只想给它一个兜底返回值时:
using ::testing::Return; ON_CALL(foo, GetSize()) .WillByDefault(Return(1)); // 默认返回 1 // ... 之后可以针对特定参数再写更具体的 EXPECT_CALL,会覆盖默认值这一技巧配合DefaultValue<T>::SetFactory可以大幅减少测试样板代码,特别适合返回值有"合理缺省值"的接口(说明见 gmock_cheat_sheet.md 的 "Setting Default Actions" 一节)。
总结:10 个技巧速查表
| # | 技巧 | 核心 API |
|---|---|---|
| 1 | 内置 Matcher 描述参数 | EqLtGeNotNull |
| 2 | 通配符偷懒 | _An<type>() |
| 3 | 组合 Matcher | AllOfAnyOfNot |
| 4 | 消除重载歧义 | TypedEqSafeMatcherCast |
| 5 | 自定义 Matcher | MATCHER_P/MATCHER_P2 |
| 6 | 返回值三兄弟 | ReturnReturnRefReturnPointee |
| 7 | 多动作串联 | DoAll |
| 8 | 存参数再断言 | SaveArgSetArgPointee |
| 9 | Lambda 当 Action | lambda /Invoke |
| 10 | 默认行为兜底 | ON_CALL |
🎯 最后记住一句话:Matcher 负责"精确提问",Action 负责"优雅应答"。掌握这两套 API,你就掌握了 GoogleMock 的精髓。
📚 延伸阅读(相对路径):
- 完整 Matcher 速查:docs/reference/matchers.md
- 完整 Action 速查:docs/reference/actions.md
- 官方进阶菜谱(含自定义 Matcher/Action 全套写法):docs/gmock_cook_book.md
- Matcher 头文件源码:gmock-matchers.h
- Action 头文件源码:gmock-actions.h
- 初学者入门:docs/gmock_for_dummies.md
【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/GitHub_Trending/go/googletest
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考