9

歌模拟文档说:

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

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

4

2 回答 2

6

I have to disagree with @Marko Popovic's assessment, and believe what he's done is undefined behavior that happens to work. I have personally seen what he is doing, interleaving calls, to appear to work just fine. But I think it's undefined behavior.

Nevertheless, I need clarity from Google, so I've opened this issue here: https://github.com/google/googletest/issues/2828. Please go upvote it to get it attention, as I'd like the Googletest team to clarify this themselves.

Update: Google has responded, and declared that @Marko Popovic's answer relies on undefined behavior. It is a very common trap to fall in, however, because, as Marko has pointed out, and as I have seen too, it does indeed work (most of the time at least). The problem is that it is relying on undefined gmock behavior.

The problem with undefined behavior is it works oftentimes, but isn't technically correct, may be buggy, can lead to flaky tests, and can break for unknown reasons in the future when gmock gets updated in the future. In short: undefined behavior is not future-proof and not cross-platform. It may also result in race conditions or otherwise not always work. Therefore, don't do it. Listen to Google. Their statement where they state the following is in fact correct:

In particular, you mustn't interleave EXPECT_CALL()s and calls to the mock functions (https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)


Back to my original answer:

In this other answer of mine where I talk a lot about how to properly use multiple EXPECT_CALL()s, I state interleaving is not okay: google mock - can I call EXPECT_CALL multiple times on same mock object?


[QUOTE FROM MY OWN WRITING START]

Question 3: Can I call EXPECT_CALL to set some expectations on a mock method, call the mock method, then call EXPECT_CALL on the method again to change the expectations, then call the mock method again?

This question wasn't even explicitly asked by the OP, but the only reason I found this page is because I was searching for this answer for many hours and couldn't find it. My Google search was "[gmock multiple expect_call][10]." Therefore, others asking this question will also fall on this page and need a conclusive answer.

A: NO, you can NOT do this! Although it may seem to work in testing, according to Google, it produces undefined behavior. See general rule #2 above!

"Important note: gMock requires expectations to be set before the mock functions are called, otherwise the behavior is undefined. In particular, you mustn't interleave EXPECT_CALL()s and calls to the mock functions" (https://github.com/google/googletest/blob/master/googlemock/docs/for_dummies.md#using-mocks-in-tests)

Therefore, this is NOT ALLOWED!

[QUOTE FROM MY OWN WRITING END]

Update (see the comments below my answer):

Perhaps this is NOT undefined behavior though!? All I added was 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(...);
}

References:

  1. @Marko Popovic's assessment
  2. Googletest issue where I ask for clarity about this: https://github.com/google/googletest/issues/2828
  3. [my own answer] google mock - can I call EXPECT_CALL multiple times on same mock object?
于 2020-04-27T19:49:18.857 回答
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(...);
}

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

于 2016-10-18T09:19:58.737 回答