Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to ignore some mock method calls? #126

Open
gambr opened this issue Feb 10, 2025 · 5 comments
Open

How to ignore some mock method calls? #126

gambr opened this issue Feb 10, 2025 · 5 comments

Comments

@gambr
Copy link

gambr commented Feb 10, 2025

Supposing I have two tests that use the same mock object. In both tests there is a call that causes the call of both methods of the mock. The point is that I want to verify just one mock method for each test. Is there a way to ignore the other mock method calls?
I suppose one option is to add in each test a MOCK_EXPECT of the other method too but what if the methods of the mock are 10? It is not a good idea to add nine MOCK_EXPECT for all the other methods not strictly involved in the current test. Another option is to create a different mock one for each test, but also this way is a little annoying. Is there a clean way to solve this problem?

MOCK_BASE_CLASS(MyObjectMock, MyObject)
{
    MOCK_METHOD(a, 0);
    MOCK_METHOD(b, 0);
};

BOOST_AUTO_TEST_CASE(First) {
    MyObjectMock mock;
    ClassToTest toTest(mock);

    MOCK_EXPECT(processMock.a()).once().returns(2);
    toTest.update();

    BOOST_CHECK_EQUAL(toTest.getA(), 2);
}

BOOST_AUTO_TEST_CASE(Second) {
    MyObjectMock mock;
    ClassToTest toTest(mock);
    
    MOCK_EXPECT(processMock.b()).once().returns(3);
    toTest.update();

    BOOST_CHECK_EQUAL(toTest.getB(), 3);
}

I the previous example toTest.update() calls both a() and b() of the mock.

Thanks

@Flamefire
Copy link
Collaborator

Unfortunately there is no way to ignore those calls. The semantic of MOCK_EXPECT is exactly that: "Expect that this is called".
If anything else is called that is not (explicitly) expected it is an error.

Consider the alternative: You want to make sure only method a is called but not b, c or d then you'd need to use MOCK_UNEXPECT(b) etc.
So you'd have the same issue.

Not only that: Your method b has a return value. Without a MOCK_EXPECT it won't know what to return. So what is it supposed to do?

Keep in mind that mock objects don't need to be derived classes, so things like "just call the parent method" don't work in the general case. This gives a lot of flexibility to the user but also makes it a bit harder at some points on the library and user side.

@gambr
Copy link
Author

gambr commented Feb 10, 2025

Thanks Alexander, I can understand what you mean. What I had in mind was something in the MOCK_BASE_CLASS like: MOCK_METHOD_DEFAULT where a return value is specified and that means if no MOCK_EXPECT is defined in the test then this one is used.

@Flamefire
Copy link
Collaborator

I don't think that would be easy enough to implement to be worth the work because expectations are stored in instances of the class.

You can have a method set_defaults(MyObjectMock& obj) where you can set up expectations.
Only issue is that MOCK_EXPECT always adds.

I could imagine a macro MOCK_EXPECT_DEFAULT or MOCK_DEFAULT_BEHAVIOR that works just like MOCK_EXPECT but gets removed if any MOCK_EXPECT is installed. You can then use that in set_defaults

In any case I still lean towards the original design of being explicit in the test. Every expected call must be marked as such. This is the safest behavior at least.

@gambr
Copy link
Author

gambr commented Feb 10, 2025

Your idea to have a default expectation that can be replaced by an explicit MOCK_EXPECT makes sense. I agree with you to be explicit in the test but how else would you solve my case?

  1. implement a divverent mock class one for each test
  2. add all the other MOCK_EXPECT

In case 1, it does not change much wrt MOCK_EXPECT_DEFAULT because in the absence of this I have to implement a base class where default values are returned for each method (a Stub). Each different mock will inherit then from the Stub. So, the same idea behind MOCK_EXPECT_DEFAULT but without the flexibility to have just one mock class.

Case 2 is worst from my point of view because, it is true to be explicit in the tests but even more tests should be simple, clear and easy to read.

@Flamefire
Copy link
Collaborator

I guess different people will have different opinions on the "clear" part:

// Common
MOCK_EXPECT(processMock.a()).returns(2);
MOCK_EXPECT(processMock.b()).returns(3);
MOCK_EXPECT(processMock.c()).returns(4);
// Important
MOCK_EXPECT(processMock.d()).once().returns(42);

Having this with some variations in each test method is very clear in what happens: Those methods may be called, we don't care how and how often. This is mostly what "implement a base class where default values are returned for each method (a Stub)." also achieves but with more flexibility.

Others might argue that this makes it easy to forget removing one of the "Common" setups when adding one of the "Important" ones. This is what MOCK_EXPECT_DEFAULT would solve. To avoid the repetition you can move this to a helper method.

Having defaults defined for the class won't be easy to implement as the state needs to be stored somewhere and it would be quite messy to integrate some kind of default state without running into any corner case that might break someone in a specific situation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants