Reputation: 9173
Assume that I want to test following function:
template<typename Pack>
bool is_valid(const Pack& pack) {
if (pack.a() > 0)
return pack.b();
return false;
};
I will write a small mocker for Pack
as follows:
class PackMock {
public:
MOCK_METHOD(int, a, (), (const));
MOCK_METHOD(bool, b, (), (const));
};
and now I try to write test this method with in test function:
TEST(MyTests, IsValidTest) {
PackMock p;
EXPECT_CALL(p, a())
.WillOnce(Return(0))
.WillOnce(Return(1))
.WillOnce(Return(20));
EXPECT_CALL(p, b())
.WillOnce(Return(true))
.WillOnce(Return(false))
.WillOnce(Return(true));
EXPECT_FALSE(is_valid(p));
EXPECT_FALSE(is_valid(p));
EXPECT_TRUE(is_valid(p));
}
At the first sight, this code should be OK. I have 3 expectation and my mocker returns 3 different set of values and this is what happens in real application running (data-wise).
But if you run this code, you will get error that mocker expectations is not satisfied (it says that b()
is expected to call 3 times while it is called twice). The reason is that when first test happens, a()
returns 0
, and because of if
condition, b()
is not called at all. So at the end, everything is messed up.
Now I wonder if there is a way to for example connect these expectations together or I should always set my expectations based on how code works rather than how my data works? I personally think that the later should be correct. For example, if a()
in mocker called we make sure that at we always satisfy 1 expectation in b()
? or for example we chain these expectations together in a way that b()
is expected only if a() > 0
?
Upvotes: 1
Views: 428
Reputation: 1186
Firstly, you can use a testing::Sequence
instance to keep track of mock calls in sequence. You can also directly define a InSequence
for the test as in the docs.
Secondly, your first EXPECT_CALL
does not call the b()
mock call, because a()
returns 0
which then never evaluates the return pack.b()
.
As an example found here, I've indicated how you can use a Sequence
class in your example as follows:
TEST(MyTests, IsValidTest) {
// Setup the mock object
PackMock p;
// Keep track of the sequence of events
// using a sequence class
testing::Sequence sequence;
// # First is_valid() - a
EXPECT_CALL(p, a())
.InSequence(sequence)
.WillOnce(Return(0));
// # First is_valid() - b - not called
// EXPECT_CALL(p, b())
// .InSequence(sequence)
// .WillOnce(Return(true));
// # Second is_valid() - a
EXPECT_CALL(p, a())
.InSequence(sequence)
.WillOnce(Return(1));
// # Second is_valid() - b
EXPECT_CALL(p, b())
.InSequence(sequence)
.WillOnce(Return(false));
// # Third is_valid() - a
EXPECT_CALL(p, a())
.InSequence(sequence)
.WillOnce(Return(20));
// # Third is_valid() - b
EXPECT_CALL(p, b())
.InSequence(sequence)
.WillOnce(Return(true));
// Act - Initiate the calls and inject the
// mock object as a dependency
// # First call - a() returns 0,
// thus returns false (but() b is not called)
EXPECT_FALSE(is_valid(p));
// # Second call - a() returns 1,
// thus returns false, because b returns false
EXPECT_FALSE(is_valid(p));
// # Third call - a() returns 20,
// thus returns true, because b returns true
EXPECT_TRUE(is_valid(p));
}
This will provide the following output:
Running main() from /opt/compiler-explorer/libs/googletest/release-1.10.0/googletest/src/gtest_main.cc
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from MyTests
[ RUN ] MyTests.IsValidTest
[ OK ] MyTests.IsValidTest (0 ms)
[----------] 1 test from MyTests (0 ms total)
[----------] Global test environment tear-down
[==========] 1 test from 1 test suite ran. (0 ms total)
[ PASSED ] 1 test.
Thus, to answer your question:
Is it possible to depend expected calls in gMock together?
Yes, using a testing::Sequence
class with or just using testing::InSequence()
Thus, if you have calls depending on each other in your test, use a sequence class instance(s) to link them.
Upvotes: 2