google mock - can I call EXPECT_CALL multiple times on same mock object?

Bob picture Bob · May 17, 2017 · Viewed 15.4k times · Source

If I call EXPECT_CALL twice on the same mock object in the same TEST_F . . . what happens?

Are the expectations appended to the mock object or does the second call erase the effects of the first call?

I found The After Clause which appears to imply that multiple calls to same mock + EXPECT_CALL are allowed.

Answer

luantkow picture luantkow · May 17, 2017

Yes, you can call EXPECT_CALL on the same mock object multiple times. As long as you assure that all EXPECT_CALL were called before the mocked methods were actually used. Otherwise your test will rely on undefined behavior. From ForDummies:

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.

How multiple calls will be handled? The documentation is really straightforward. From ForDummies:

By default, when a mock method is invoked, Google Mock will search the expectations in the reverse order they are defined, and stop when an active expectation that matches the arguments is found (you can think of it as "newer rules override older ones.").

Let's consider what this means for the gMock user, by checking some examples. I assume that we have a file with following header:

#include <gmock/gmock.h>

using namespace ::testing;

struct SomeMock
{
    MOCK_CONST_METHOD1(foo, void(int));
};

The simplest example of passing test that calls EXPECT_CALL multiple times:

TEST(Examples, DifferentArgumentsGoingToBeOk)
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); // exp#1
    EXPECT_CALL(mock, foo(5)).Times(1); // exp#2

    mock.foo(4); // call#1
    mock.foo(5); // call#2
}

The tests works intuitively:

  • call#1 does not match with exp#2 so exp#1 is tried and matches.
  • call#2 matches with exp#2.

Both calls matched exactly once, thus they are considered satisfied and the test passes.

The tricky part starts when multiple EXPECT_CALL are able to match the call. Let's consider the following example:

TEST(Examples, TheSameArgumentsGoingToFail) // Test fails!
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}
  • The call#1 matches the exp#2. gMock stops at first matched expectation, it won't check the exp#1 at all.
  • The call#2 matches the exp#2. Again the exp#1 does not have chance to be matched.

As a result the test fails as the exp#2 gets matched twice instead of once and exp#1 is not matched at all. All that is printed in the test output:

/tmp/so/main.cpp:26: Failure // exp#2
Mock function called more times than expected - returning directly.
    Function call: foo(4)
         Expected: to be called once
           Actual: called twice - over-saturated and active
/tmp/so/main.cpp:25: Failure // exp#1
Actual function call count doesn't match EXPECT_CALL(mock, foo(4))...
         Expected: to be called once
           Actual: never called - unsatisfied and active

Also, it is important, that adding new expectancy won't disable or remove old ones. They are still able to fail your test!

TEST(Examples, NewExpectCallDoesNotEraseThePreviousOne) // Test fails!
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); // exp#1
    EXPECT_CALL(mock, foo(4)).Times(2); // exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

Both call#1 and call#2 matches the exp#2. As a result the exp#2 is satisfied, but the test will fail as exp#1 was not matched enough times.

If for some reason, you need to write a test like TheSameArgumentsGoingToFail, you can use a number of techniques to prevent exp#2 from matching second time. Please refer to the documentation InSequence usage, RetiresOnSaturation:

TEST(Examples, InSequenceExample)
{
    SomeMock mock;

    Sequence seq;

    EXPECT_CALL(mock, foo(4)).Times(1).InSequence(seq); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1).InSequence(seq); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

TEST(Examples, InSequenceExampleSecondApproach)
{
    SomeMock mock;

    InSequence seq;

    EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

TEST(Examples, RetiresOnSaturationExample)
{
    SomeMock mock;

    EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1).RetiresOnSaturation(); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}

TEST(Examples, AfterExample)
{
    SomeMock mock;

    auto& exp1 = EXPECT_CALL(mock, foo(4)).Times(1); //exp#1
    EXPECT_CALL(mock, foo(4)).Times(1).After(exp1); //exp#2

    mock.foo(4); // call#1
    mock.foo(4); // call#2
}