【发布时间】:2017-02-26 14:29:08
【问题描述】:
重要提示:Google Mock 要求在调用模拟函数之前设置期望,否则行为是未定义。特别是,您不能将 EXPECT_CALL() 和对模拟函数的调用交错。
有人知道这个限制背后的任何细节吗?我有一些单元测试肯定违反了这条规则,但似乎运行正常。
【问题讨论】:
标签: c++ unit-testing googletest googlemock expectations
重要提示:Google Mock 要求在调用模拟函数之前设置期望,否则行为是未定义。特别是,您不能将 EXPECT_CALL() 和对模拟函数的调用交错。
有人知道这个限制背后的任何细节吗?我有一些单元测试肯定违反了这条规则,但似乎运行正常。
【问题讨论】:
标签: c++ unit-testing googletest googlemock expectations
我不得不不同意@Marko Popovic's assessment,并相信他所做的是碰巧起作用的未定义行为。 我亲眼看到他正在做的事情,交错调用,看起来工作得很好。但我认为这是未定义的行为。
不过,我需要 Google 澄清一下,所以我在这里打开了这个问题:https://github.com/google/googletest/issues/2828。请投票以引起关注,因为我希望 Googletest 团队自己澄清这一点。
更新:Google has responded,并声明@Marko Popovic 的回答依赖于未定义的行为。然而,这是一个非常常见的陷阱,因为正如 Marko 指出的那样,正如我也看到的那样,它确实有效(至少在大多数情况下)。问题是它依赖于未定义的 gmock 行为。
未定义的行为的问题是它经常工作,但在技术上不正确,可能有错误,可能导致不稳定的测试,并且在将来 gmock 更新时可能会因未知原因而中断将来。简而言之:未定义的行为不是面向未来的,也不是跨平台的。它也可能导致竞争条件或不总是有效。因此,不要这样做。听谷歌。他们陈述以下内容的陈述实际上是正确的:
特别是,您不能将
EXPECT_CALL()s 和对模拟函数的调用交错 (https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)
回到我原来的答案:
在我的另一个答案中,我谈到了 很多关于如何正确使用多个 EXPECT_CALL()s 的内容,我声明交错是不行的:google mock - can I call EXPECT_CALL multiple times on same mock object?
[QUOTE FROM MY OWN WRITING START]
问题3:我可以调用EXPECT_CALL对mock方法设置一些期望值,调用mock方法,然后再次调用方法上的EXPECT_CALL改变期望值,然后再调用mock方法吗?
OP 甚至没有明确提出这个问题,但我找到这个页面的唯一原因是因为我花了好几个小时搜索这个答案却找不到。我的 Google 搜索是“[gmock multiple expect_call][10]”。因此,其他提出此问题的人也将落在此页面上,需要一个确凿的答案。
A:不,你不能这样做!尽管它可能似乎在测试中起作用,但根据 Google 的说法,它会产生未定义的行为。请参阅上面的一般规则 #2!
"重要提示: gMock 要求在调用模拟函数之前设置期望,否则行为是未定义。特别是,你不能交错
EXPECT_CALL()s 和调用模拟函数" (https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)
因此,这是不允许的!
[引用我自己的写作结尾]
也许这不是未定义的行为!?我只添加了Mock::VerifyAndClearExpectations(&myMockObj)。
TEST(FooTest, testCaseName)
{
MyMock myMockObj;
...
EXPECT_CALL(myMockObj, myMethod(_)).WillOnce(Return(true));
testMethod();
ASSERT_THAT(...);
Mock::VerifyAndClearExpectations(&myMockObj); // <== NOTICE THIS ADDED LINE!
EXPECT_CALL(myMockObj, myMethod(_)).WillOnce(Return(false));
testMethod();
ASSERT_THAT(...);
}
【讨论】:
EXPECT_CALL()。您必须改为致电 Mock::VerifyAndClearExpectations(&mock_obj); 或 Mock::VerifyAndClear(&mock_obj);。
Mock::VerifyAndClearExpectations(&mock_obj) 之前 为该模拟对象在EXPECT_CALL() 的每个新调用中添加,那么交错行为不再是未定义的!我会将此添加到我的答案中。
Do not set new expectations after verifying and clearing a mock after its use. Setting expectations after code that exercises the mock has undefined behavior. 看起来 Mock::VerifyAndClearExpectations() 没有帮助。
正如文档所说,重要的是在调用方法之前设置期望。然而,这并不意味着
不可能将EXPECT_CALL 和模拟方法的调用交错。只是你必须自己清除对模拟对象的期望。例如,假设我们有以下模拟
类
class MyMock : public bravo::IRealClass
{
public:
...
MOCK_METHOD1(myMethod, bool(int));
...
}
现在,假设对方法 testMethod 的调用调用了一次 myMethod,您可以在测试中编写如下内容:
TEST(FooTest, testCaseName)
{
MyMock myMockObj;
...
EXPECT_CALL(myMockObj, myMethod(_)).WillOnce(Return(true));
testMethod();
ASSERT_THAT(...);
// Explicitly clear expectations and set new ones.
Mock::VerifyAndClearExpectations(&myMockObj);
EXPECT_CALL(myMockObj, myMethod(_)).WillOnce(Return(false));
testMethod();
ASSERT_THAT(...);
}
这很好,因为模拟对象可以再次可靠地重用。但是,如果您忽略明确清除期望,您将进入未定义行为的领域。与未定义行为的情况一样,它可能不会崩溃,甚至在某些情况下可能会起作用,但如果你的代码中有类似的东西,你一定要修复它。
【讨论】:
testMethod() 在第二个EXPECT_CALL() 之前被调用,因为这只是测试第一个EXPECT_CALL() 再次出现,而是在第二个EXPECT_CALL() 之后没有另一个testMethod()。换句话说,@Marko Popovic 是说对于每个EXPECT_CALL(),您必须至少调用一次模拟方法。这当然不是真的,因为您可以使用.Times(0) 执行EXPECT_CALL() 并且没有问题。不过,在这种情况下,他可能是对的……