Afshin
Afshin

Reputation: 9173

Is it possible to depend expected calls in gMock together?

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

Answers (1)

Jan Gabriel
Jan Gabriel

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

Related Questions