Search code examples
c++googlemockexpectations

Google Mock: why is a partial ordering of expectations harder to satisfy than a total ordering?


I'm mostly using ordered expectations with GoogleMock, so all EXPECT_CALLs were written inside the scope of a testing::InSequence object.

Now I want to relax the ordering so I split the expectations in 2 sequences. You would say the test should pass, but no - it fails, complaining about unmet preconditions. How should I reason about this?

Edit: Reduced version of my code:

//InSequence s;                                     // uncomment this and it works
for (int i = 1; i <= 2; ++i)
{
    {
        //InSequence s;                             // uncomment this and it doesn't work

        EXPECT_CALL(mock1, produceMessage(_))
            .WillOnce(DoAll(SetArgReferee<0>(val1), Return(false)))
            .WillOnce(DoAll(SetArgReferee<0>(val2), Return(false)))
            .WillOnce(DoAll(SetArgReferee<0>(val2), Return(false)));

        EXPECT_CALL(mock2, handleEvent(A<MyType>()));
        EXPECT_CALL(mock2, handleMessage(NotNull()));
    }
}

So if the InSequence is nested inside the for loop, I should have a partial order, which is a relaxed requirement, compared to the case when the InSequence is on the outside.

Error I'm getting:

Mock function called more times than expected - returning default value.
    Function call: handleMessage(0xd7e708)
          Returns: false
         Expected: to be called once
           Actual: called twice - over-saturated and active

And then, at the end of the test:

Actual function call count doesn't match EXPECT_CALL(mock2, handleMessage(NotNull()))...
         Expected: to be called once
           Actual: never called - unsatisfied and active

Solution

  • After some more progress on the GoogleMock learning curve, I'll try to answer my own question in a way that is general enough to be helpful.

    Let's consider the following example of totally ordered expectations:

    {
        InSequence s;
    
        EXPECT_CALL(mock1, methodA(_));     // expectation #1
        EXPECT_CALL(mock2, methodX(_));     // expectation #2
    
        EXPECT_CALL(mock1, methodA(_));     // expectation #3
        EXPECT_CALL(mock2, methodY(_));     // expectation #4
    }
    

    Now, let's slice the ordering in two.

    {
        InSequence s;
    
        EXPECT_CALL(mock1, methodA(_));     // expectation #1
        EXPECT_CALL(mock2, methodX(_));     // expectation #2
    }
    
    {
        InSequence s;
    
        EXPECT_CALL(mock1, methodA(_));     // expectation #3
        EXPECT_CALL(mock2, methodY(_));     // expectation #4
    }
    

    The intention is to allow expectations from the two sequences to "merge", i.e. to have expectation #1 as precondition for #2 and #3 for #4 but not more than that.

    However, the following sequence of calls will satisfy the totally ordered expectations but not the "partially ordered" ones:

    mock1.methodA();   // call #1
    mock2.methodX();   // call #2
    mock1.methodA();   // call #3
    mock2.methodY();   // call #4
    

    Reason: it is obvious why the totally ordered expectations are satisfied: the example just satisfies them in the order they are written. Being InSequence, they retire as soon as they are satisfied.

    However, the "partially ordered" scenario doesn't work because call #1 will satisfy expectation #3, then call #2 will match against expectation #2, which can't be met because it has expectation #1 as precondition. Even though technically, expectations #1 and #3 are identical, they are satisfied in reverse order of writing, since they don't belong to the same ordering, hence the failure.

    I have the feeling that this phenomenon is not well enough documented by Google Mock. I'm still looking for a better formalization. I suspect that there's something wrong with the "partial order" concept as used here.