What's the point of verifying the number of times a function is called with Mockito?

Viewed 1973

In my understanding, code testing is to test whether results are right, like a calculator, I need to write a test case to verify if the result of 1+1 is 2.

But I have read many test cases about verifying the number of times a method is called. I'm very confused about that. The best example is what I just saw in Spring in Action:

public class BraveKnight implements Knight {
    private Quest quest;
    public BraveKnight(Quest quest) { 
        this.quest = quest; 
    }
    public void embarkOnQuest() {
        quest.embark(); 
    }
}

public class BraveKnightTest {
    @Test 
    public void knightShouldEmbarkOnQuest() { 
        Quest mockQuest = mock(Quest.class); 
        BraveKnight knight = new BraveKnight(mockQuest); 
        knight.embarkOnQuest(); 
        verify(mockQuest, times(1)).embark(); 
    }
}

I really have no idea about why they need to verify the embark() function is called one time. Don't you think that embark() will certainly be invoked after embarkOnQuest() is called? Or some errors will occur, and I will notice error messages in the logs, which show the error line number, that can help me quickly locate the wrong code.

So what's the point of verifying like above?

4 Answers

The need is simple: to verify that the correct number of invocations were made. There are scenarios in which method calls should not happen, and others in which they should happen more or less than the default.

Consider the following modified version of embarkOnQuest:

public void embarkOnQuest() {
    quest.embark(); 
    quest.embarkAgain(); 
}

And suppose you are testing error cases for quest.embark():

@Test 
public void knightShouldEmbarkOnQuest() { 
    Quest mockQuest = mock(Quest.class); 
    Mockito.doThrow(RuntimeException.class).when(mockQuest).embark();
    ...
}

In this case you want to make sure that quest.embarkAgain is NOT invoked (or is invoked 0 times):

verify(mockQuest, times(0)).embarkAgain(); //or verifyZeroInteractions

Of course this is one other simple example. There are many other examples that could be added:

  • A database connector that should cache entries on first fetch, one can make multiple calls and verify that the connection to the database was called just once (per test query)
  • A singleton object that does initialization on load (or lazily), one can test that initialization-related calls are made just once.

Consider the following code:

public void saveFooIfFlagTrue(Foo foo, boolean flag) {
    if (flag) {
        fooRepository.save(foo);
    }
}

If you don't check the number of times that fooRepository.save() is invoked , then how can you know whether this method is doing what you want it to?

This applies to other void methods. If there is no return to a method, and therefore no response to validate, checking which other methods are called is a good way of validating that the method is behaving correctly.

Good question. You raise a good point that mocking can be overly circuitous when you can just check the results. However, there are contexts where this does lead to more robust tests.

For example, if a method needs to make a call to an external API, there are several problems with simply testing the result:

  • Network I/O is slow. If you have many checks like this, it will slow down your test case
  • Any round-trip like this would have to rely on the code making the request, the API, and the code interpreting the API's response all to work correctly. This is a lot of failure points for a single test.
  • If something stupid happens and you accidentally make multiple requests, this could cause performance issues with your program.

To address your sub-questions:

Don't you think that embark() will certainly be invoked after embarkOnQuest() called?

Tests also have value in letting you refactor without worry about breaking things. This is obvious now, yes. Will it be obvious in 6 months?

I really have no idea about why they need to verify the embark() function is called one time

Verifying an invocation on a mock for a specific number of times is the standard way how Mockito works as you invoke Mockito.verify(). In fact this :

verify(mockQuest, times(1)).embark(); 

is just a verbose way to write :

verify(mockQuest).embark(); 

In a general way, the verification for a single call on the mock is what you need.
In some uncommon scenarios you may want to verify that a method was invoked a specific number of times (more than one).
But you want to avoid using so specific verifications.
In fact you even want to use verifying as few as possible.

If you need to use verifying and besides the number of invocation on the mock, it generally means two things : the mocked dependency is too much coupled to the class under test and or the method under test performs too many unitary tasks that produce only side effects.
The test is so not necessary straight readable and maintainable. It is like if you coded the mock flow in the verifying invocations.
And as a consequence it also makes the tests more brittle as it checks invocation details not the overall logic and states.
In most of cases, a refactoring is the remedy and cancel the requirement to specify a number of invocation.

I don't tell that it is never required but use it only as it happens to be the single decent choice for the class under test.

Related