【问题标题】:Interleaving EXPECT_CALL()s and calls to the mock functions交错 EXPECT_CALL() 和调用模拟函数
【发布时间】:2017-02-26 14:29:08
【问题描述】:

Google Mock documentation 说:

重要提示:Google Mock 要求在调用模拟函数之前设置期望,否则行为是未定义。特别是,您不能将 EXPECT_CALL() 和对模拟函数的调用交错。

有人知道这个限制背后的任何细节吗?我有一些单元测试肯定违反了这条规则,但似乎运行正常。

【问题讨论】:

    标签: c++ unit-testing googletest googlemock expectations


    【解决方案1】:

    我不得不不同意@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)

    因此,这是不允许的!

    [引用我自己的写作结尾]

    更新(请参阅我的答案下方的 cmets):

    也许这不是未定义的行为!?我只添加了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(...);
    }
    

    参考资料:

    1. @Marko Popovic's assessment
    2. Googletest 问题,我要求澄清:https://github.com/google/googletest/issues/2828
    3. [我自己的回答]google mock - can I call EXPECT_CALL multiple times on same mock object?

    【讨论】:

    • 感谢您进一步澄清。只是对第三个问题的评论:改变期望绝对是可能的,见github.com/google/googletest/blob/master/googlemock/docs/…
    • 啊...我明白了,但不是通过在该模拟方法上再次调用 EXPECT_CALL()。您必须改为致电 Mock::VerifyAndClearExpectations(&amp;mock_obj);Mock::VerifyAndClear(&amp;mock_obj);
    • 啊...有趣!这也意味着,如果您只是简单地将Mock::VerifyAndClearExpectations(&amp;mock_obj) 之前 为该模拟对象在EXPECT_CALL() 的每个新调用中添加,那么交错行为不再是未定义的!我会将此添加到我的答案中。
    • cheat sheet 中的当前措辞: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() 没有帮助。
    【解决方案2】:

    正如文档所说,重要的是在调用方法之前设置期望。然而,这并不意味着 不可能将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(...);
    }
    

    这很好,因为模拟对象可以再次可靠地重用。但是,如果您忽略明确清除期望,您将进入未定义行为的领域。与未定义行为的情况一样,它可能不会崩溃,甚至在某些情况下可能会起作用,但如果你的代码中有类似的东西,你一定要修复它。

    【讨论】:

    • 为什么最后一组期望会成为问题?这不像 EXPECT_CALL 在第一个 testMethod() 之后过期。否则它将无法检查该方法是否确实只被调用了一次。
    • @DBendrenko,在我看来,根据@Marko Popovic 的说法,第二个问题并不是真的testMethod() 在第二个EXPECT_CALL() 之前被调用,因为这只是测试第一个EXPECT_CALL() 再次出现,而是在第二个EXPECT_CALL() 之后没有另一个testMethod()。换句话说,@Marko Popovic 是说对于每个EXPECT_CALL(),您必须至少调用一次模拟方法。这当然不是真的,因为您可以使用.Times(0) 执行EXPECT_CALL() 并且没有问题。不过,在这种情况下,他可能是对的……
    • ...但这完全违背了谷歌所说的:“特别是,你不能将 EXPECT_CALL()s 和对模拟函数的调用交错”,所以我很难相信他是正确,即使我已经看到这样的构造也可以工作。我认为@Marko 是错误的,他在第一种情况下显示的是未定义的行为 确实 碰巧起作用,而在第二种情况下,未定义的行为 doesn't碰巧工作。我想要一个来自谷歌的权威答案。让我们在Googletest repo 上打开一个问题。
    • @Marko Popovic,Google has responded,并声明您所做的事情是未定义的行为。然而,这是一个非常常见的陷阱,因为正如您所指出的,它确实有效。问题是它依赖于未定义的行为,因此可能导致竞争条件、错误、导致不稳定的测试,或者不总是有效。因此,我不得不对您的回答投反对票。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-08-05
    • 1970-01-01
    • 2020-05-08
    • 2019-11-21
    • 2022-08-23
    • 2022-08-10
    • 2015-12-27
    相关资源
    最近更新 更多