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
}