如何用 GoogleMock NiceMock 和 StrictMock 控制 uninteresting call 警告
【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/GitHub_Trending/go/googletest
在写 GoogleMock 测试时,一个常见的现象是:mock 对象的某个方法没有设置任何EXPECT_CALL,却仍然被被测代码调用了。gMock 把这种调用称为 uninteresting call,默认的 mock 对象(行为等价于NaggyMock)会执行该方法的默认动作(可用ON_CALL()指定),并额外打印一条 "uninteresting" 警告。这条警告本身不是错误,但有时你希望彻底静默它,有时又希望它直接让测试失败。本文基于 GoogleMock 实战手册 和 参考文档,说明如何分别用NiceMock<T>和StrictMock<T>来控制这两类行为,以及各自的适用边界。
动手前先分清 uninteresting call 和 unexpected call
这两种调用在 gMock 中是完全不同的概念(见 The Nice, the Strict, and the Naggy 一节的解释):
- uninteresting call:方法
x.Y(...)上一个EXPECT_CALL(x, Y(...))都没有设置,说明测试对该方法本身不关心。按 gMock 的规则,"不说什么"就意味着"没有约束",所以 uninteresting call 默认不是错误,只产生警告——因为可能暗示测试作者忘了写约束。 - unexpected call:已经设置了某些
EXPECT_CALL(x, Y(...)),但没有一个与本次调用匹配。unexpected call总是错误,因为被测代码的行为不符合测试预期。
NiceMock和StrictMock只作用于 uninteresting call,不影响 unexpected call 的严重级别。这一点直接决定了后文两种包装的用法:nice mock 不会让原本失败的测试通过,strict mock 则可能改变测试结果。
用 NiceMock 静默 uninteresting call 警告
NiceMock<T>表示一个会对 uninteresting call 抑制警告的 mock 对象(见 NiceMock 参考条目)。它是T的子类,可以放在任何接受T的地方使用;同时它"继承"了T的构造函数,所以可以接受T构造函数所需的任意实参。
假设测试原本这样写:
TEST(...) { MockFoo mock_foo; EXPECT_CALL(mock_foo, DoThis()); ... code that uses mock_foo ... }只要mock_foo上DoThis()之外的方法被调用,就会收到警告。改用NiceMock<MockFoo>即可抑制这类警告:
using ::testing::NiceMock; TEST(...) { NiceMock<MockFoo> mock_foo; EXPECT_CALL(mock_foo, DoThis()); ... code that uses mock_foo ... }如果MockFoo的构造函数需要参数,直接透传即可:
using ::testing::NiceMock; TEST(...) { NiceMock<MockFoo> mock_foo(5, "hi"); // Calls MockFoo(5, "hi"). EXPECT_CALL(mock_foo, DoThis()); ... code that uses mock_foo ... }替代路径:如果你只想对个别方法关闭警告,而不想静默整个 mock 对象,手册给出的做法是为该方法补一条EXPECT_CALL(...).Times(AnyNumber()),例如:
using ::testing::_; using ::testing::AnyNumber; EXPECT_CALL(mock_registry, GetDomainOwner(_)) .Times(AnyNumber()); // catches all other calls to this method.手册同时明确警告:不要为了消除警告而盲目地加EXPECT_CALL(...)(不带Times(AnyNumber())),那会造出一个难以维护的测试。
判断依据:手册指出,nice mock 只是"话少"了,除此之外与默认 mock 行为一致——如果某个测试用默认 mock 会失败,换成 nice mock 一样会失败,反之亦然。所以验证NiceMock是否生效,看的是警告消失且测试结果不变,而不是测试结果发生变化。
用 StrictMock 把 uninteresting call 变成失败
StrictMock<T>的用法与NiceMock<T>类似(见 StrictMock 参考条目),区别是它把所有 uninteresting call 从警告升级为测试失败:
using ::testing::StrictMock; TEST(...) { StrictMock<MockFoo> mock_foo; EXPECT_CALL(mock_foo, DoThis()); ... code that uses mock_foo ... // The test will fail if a method of mock_foo other than DoThis() // is called. }只要被测代码调用了mock_foo上除DoThis()之外的任何方法,测试就会失败。与 nice mock 不同,手册明确指出:把 mock 改成 strict 可能会改变测试结果,这正是使用它的目的,也是使用它需要小心的原因。
一个典型的配合场景来自手册中"uninteresting vs unexpected"一节:只想验证GetDomainOwner("google.com")的行为,但允许该方法用其他参数被调用。标准做法是加一条"兜底"EXPECT_CALL:
EXPECT_CALL(mock_registry, GetDomainOwner(_)) .Times(AnyNumber()); // catches all other calls to this method. EXPECT_CALL(mock_registry, GetDomainOwner("google.com")) .WillRepeatedly(Return("Larry Page"));注意两条EXPECT_CALL的先后顺序很重要:新写的EXPECT_CALL优先于旧写的生效。这里_是匹配任意值的通配 matcher——GetDomainOwner("google.com")命中第二条,其余参数命中第一条。
必须了解的三个限制
NiceMock和StrictMock都有一些 C++ 层面带来的限制,使用前的 mock 类设计要对照检查:
- 只对被测类内直接用
MOCK_METHOD宏定义的 mock 方法生效。如果 mock 方法定义在T的基类中,"nice"或"strict"修饰对它们可能不起作用,具体取决于编译器。 - 不支持嵌套。
NiceMock<StrictMock<MockFoo>>这类嵌套写法不被支持。参考文档也相应限制:NiceMock<T>、NaggyMock<T>、StrictMock<T>的模板参数T不能是另一个NiceMock、NaggyMock或StrictMock。 T的析构函数如果不是 virtual,修饰可能不生效,文档明确说明这一点是已知的局限。
什么时候选哪种
手册对选择给出了明确的倾向性建议,可以直接对照执行:
- naggy mock(当前默认):在开发或调试测试时使用,警告加默认行为帮你留意"是否有该设约束而未设约束的调用"。
- nice mock:多数时候使用,避免重构代码时因内部交互变化被警告刷屏。
- strict mock:只作为最后手段。
原因是 naggy 或 strict mock 倾向让测试更脆、更难维护:当你重构代码而不改变外部可见行为时,理想情况下测试不该跟着改;但代码一旦与 naggy mock 交互,可能开始被警告刷屏,与 strict mock 交互则测试直接失败、被迫修改。
验证与后续排查
仓库自带针对该行为的测试文件 gmock-nice-strict_test.cc,可以结合它观察三种 mock 在相同调用序列下的表现差异。日常验证时可以按上面的判断依据对照:
- 换成
NiceMock后,uninteresting call 警告消失,测试结果与原 mock 一致; - 换成
StrictMock后,故意触发一条未设EXPECT_CALL的调用,测试应当失败; - 若仍看到 "Uninteresting function call encountered - default action taken.." 之类的警告,gmock_faq.md 解释它只是提示信息而非错误:gMock 遇到 uninteresting call 时会 dump 调用栈,从中可以定位是哪个 mock 方法、被怎样调用的。如果认为本不该出现 uninteresting call,应顺着调用栈排查;如果你本意是禁止调用却忘了写
EXPECT_CALL(foo, Bar()).Times(0),按 Disallowing Unexpected Calls 一节补上显式的Times(0)约束,而不是靠包装类兜底。
限制方面再强调一次:以上所有行为只针对方法上没有任何EXPECT_CALL的 uninteresting call;已经设置了EXPECT_CALL但参数不匹配的 unexpected call 始终是错误,NiceMock也不会减轻它的严重级别。
【免费下载链接】googletestGoogleTest - Google Testing and Mocking Framework项目地址: https://gitcode.com/GitHub_Trending/go/googletest
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考