Mockito “when” vs. “verify”

Recently, during a code review at work there was a discussion about whether Mockito’s verify is necessary when when (or given, which is a synonym) is parameterized. The quick answer is "no".

Imagine this contrived example. I have two classes, Calculator and ComplexOperationService, where the latter requires the former. The goal is to write a unit test for ComplexOperationService and mock all usages of Calculator.

Here are the two classes.

public class Calculator {

    public int sum(final int a, final int b) {
        return a + b;
    }
}

@RequiredArgsConstructor
public class ComplexOperationService {
    
    private final Calculator calculator;

    public void doComplexOperation(final int a, final int b) {
        System.out.println(calculator.sum(a, b));
    }
}

And here are two types of tests you can write. First, only by using when. This is basically the succinct variant. In this case the mock will only return a result if it is called with the exact parameters. Otherwise: error. More on that in a bit.

@Test
public void verify_with_when()
{
    when(calculator.sum(40, 2)).thenReturn(42);
    complexOperationService.doComplexOperation(40, 2);
}

The longer version is to use verify instead. In this small example it doesn’t amount to much more, but imagine you have five whens and five corresponding verifys. At some point it is getting verbose. What is happening here is that I’m saying that I don’t care about the parameters passed to the mock. Just always return a result when the method is called. Then, in a separate step, explicitly check that the method has been called with specific parameters. This is how I prefer it, despite having to write a bit more.

@Test
public void verify_with_verify_anyInt_fails()
{
    when(calculator.sum(anyInt(), anyInt())).thenReturn(42);
    complexOperationService.doComplexOperation(20, 1);
    verify(calculator).sum(40, 2);
}

Apart from style, what are the differences? The quick answer is "error messages".

Let’s start with the verify example and make it fail.

@Test
public void verify_with_verify_anyInt_fails()
{
    when(calculator.sum(anyInt(), anyInt())).thenReturn(42);
    complexOperationService.doComplexOperation(20, 1);
    verify(calculator).sum(40, 2);
}

The corresponding error message says:

Wanted but not invoked:
calculator.sum(40, 2);
-> at com.thecodeslinger.CalculatorTest.verify_with_verify_anyInt_fails(CalculatorTest.java:49)
However, there was exactly 1 interaction with this mock:
calculator.sum(20, 1);

A method call with parameters "40" and "2" was expected but "20" and "1" have been provided. Pretty straightforward. Before moving on the the when version, let’s have a look at the error message when calculator.sum() isn’t called at all.

Wanted but not invoked:
calculator.sum(40, 2);
-> at com.thecodeslinger.CalculatorTest.verify_with_verify_anyInt_fails_no_call(CalculatorTest.java:62)
Actually, there were zero interactions with this mock.

Also, truly clear what is happening here.

Allright, now we’ll take a look at how Mockito reacts when the when cases fail.

@Test
public void verify_with_when_fails()
{
    when(calculator.sum(40, 2)).thenReturn(42);
    complexOperationService.doComplexOperation(20, 1);
}

And the error message. Wait! Is that a usage error of Mockito?

org.mockito.exceptions.misusing.PotentialStubbingProblem: 
Strict stubbing argument mismatch. Please check:
- this invocation of 'sum' method:
    calculator.sum(20, 1);
    -> at com.thecodeslinger.ComplexOperationService.doComplexOperation(ComplexOperationService.java:11)
- has following stubbing(s) with different arguments:
    1. calculator.sum(40, 2);
    -> at com.thecodeslinger.CalculatorTest.verify_with_when_fails(CalculatorTest.java:32)
Typically, stubbing argument mismatch indicates user mistake when writing tests.
Mockito fails early so that you can debug potential problem easily.
However, there are legit scenarios when this exception generates false negative signal:
- stubbing the same method multiple times using 'given().will()' or 'when().then()' API
    Please use 'will().given()' or 'doReturn().when()' API for stubbing.
- stubbed method is intentionally invoked with different arguments by code under test
    Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).

Let’s quickly also look at the message when the mock isn’t called.

org.mockito.exceptions.misusing.UnnecessaryStubbingException: 
Unnecessary stubbings detected.
Clean & maintainable test code requires zero unnecessary code.
Following stubbings are unnecessary (click to navigate to relevant line of code):
1. -> at com.thecodeslinger.CalculatorTest.verify_with_when_fails_no_call(CalculatorTest.java:39)
Please remove unnecessary stubbings or use 'lenient' strictness. More info: javadoc for UnnecessaryStubbingException class.

So… Technically Mockito is correct. But it makes it hard to determine whether there is a bug in how the tests have been written or in the logic that is being tested.

This, of course, isn’t the only difference in using when and verify. There is much more to it. For example, using verify you can say how many times it is expected that the method is being called.

// times(1) is the implicit default.
verify(calculator, times(2)).sum(40, 2);

I’m not going into more detail on what you can do with when and verify. The point of this little experiment was to find out whether it could be enough to write when and use that to verify the method has been called with the expected parameters. The quick answer is "yes".

However, as I have shown, the error message is not immediately helpful. Unless you’ve encountered this message in a similar situation before and also haven’t forgotten about it you might wonder where the error comes from all of a sudden.

Apart from the error message, I also prefer using verify to explicitly state my intention. At my former employer I have worked with the Grails framework and for testing it uses Spock. This requires you to structure tests in a specific way.

given:
// Variables, test setup

when:
// The thing that is being tested

then:
// Asserts and method call verifications

I have adopted this layout to Java and the way I do it is to specify mock invocations in the "given" section using Mockito’s given instead of when and then verify the invocations in the "then" section. Since there are no keywords in Java for given:, when: and then: I use comments to separate the three sections.

(I didn’t do it in the sample project because of brevity)

Therefore, my tests usually look like this:

@Test
public void verify_with_verify_anyInt()
{
    // Given
    given(calculator.sum(anyInt(), anyInt())).willReturn(42);

    // When
    complexOperationService.doComplexOperation(40, 2);

    // Then
    verify(calculator).sum(40, 2);
}

This helps me a lot in visually separating setup, the tested code and verification of result and mocks. I find it hard to read and understand tests so I try to make it as easy as I can. I know that adding a bunch of verify adds a lot more code. However, as long as it is separated as clearly as I do it, I still prefer it.

I’ve seen colleagues write more complex tests where asserts are baked into the when calls, e.g. to extract parameters from an invocation object.

Pseudocode:

when(calculator.sum).thenAnswer(invocation -> {
    var a = (int)invocation.getArgument(0);
    var b = (int)invocation.getArgument(1);
    assert(40, a);
    assert(2, b);
});

Of course, this kind of assert doesn’t add anything to the one-liner when(calculator.sum(40, 2)).thenReturn(42);. Just imagine there’s much more complex logic happening there. The problem I have is that verification is happening before the code under test is called. This means I have to keep the mock and the verification in my head until I see what is being tested. And then there may be more verification and I have to check two locations (or even more) to make sure everything has been tested and verified.

It’s like judge, jury and executioner, only in a different order. Judge, executioner and then jury. I’m not saying that this is a bad style and sometimes there may be legitimate reasons to do so. I, personally, try to keep my particular ordering because it helps me. I have even convinced a colleague to write similarly structured tests, not by long discussions, but simply by writing tests this way and he reviewed them.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.