Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interleaving EXPECT_CALL()s and calls to the mock functions

The Google Mock documentation says:

Important note: Google Mock 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.

Does anyone know any details behind this restriction? I have a number of unit tests which definitely violate this rule but seem to function properly, however.

like image 339
Jordan Avatar asked Oct 17 '16 14:10

Jordan


2 Answers

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?
like image 123
Gabriel Staples Avatar answered Sep 29 '22 01:09

Gabriel Staples


Just as the documentation says, what is important is that expectations are set before the method is called. However, this does not mean that it is not possible to interleave EXPECT_CALL and invocation of mock method. It's just that you have to clear the expectations on the mock object yourself. For example, assume that we have the following mock class

class MyMock : public bravo::IRealClass
{
public:
    ...
    MOCK_METHOD1(myMethod, bool(int));
    ...
}

Now, assuming that a call to method testMethod calls myMethod once, you can write something like this in a test:

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(...);
}

This will be fine since the mock object can be reliably reused again. However, if you omit to explicitly clear the expectations, you are entering the realm of undefined behavior. As is always the case with undefined behavior, it is possible that it won't crash and it might even work in some cases, but if you have have something like that in your code you should definitely fix it.

like image 39
Marko Popovic Avatar answered Sep 29 '22 00:09

Marko Popovic