Mockito ArgumentMatchers

1. Overview

This tutorial shows how to use the ArgumentMatcher and how it differs from the ArgumentCaptor.

For an introduction to the Mockito framework, please refer to this article.

2. Maven Dependencies

We need to add a single artifact:

<dependency>
    <groupId>org.mockito</groupId>
    <artifactId>mockito-core</artifactId>
    <version>2.21.0</version>
    <scope>test</scope>
</dependency>

The latest version of Mockito can be found on Maven Central.

3. ArgumentMatchers

Configuring a mocked method in various ways is possible. One of them is to return fixed values:

doReturn("Flower").when(flowerService).analyze("poppy");

In the above example, the String “Flower” is returned only when the analyze service receive the String “poppy”.

But maybe we need to respond to a wider range of values or beforehand unknown values.

In all these scenarios, we can configure our mocked methods with argument *matchers*:

when(flowerService.analyze(anyString())).thenReturn("Flower");

Now, because of the anyString argument matcher, the result will be the same no matter what value we pass to analyze. ArgumentMatchers allows us flexible verification or stubbing.

In case of a method has more than one argument, it isn’t possible to use ArgumentMatchers for only some of the argumentsMockito requires you to provide all arguments either by matchers or by exact values.

A next example is an incorrect approach to this:

abstract class FlowerService {
    public abstract boolean isABigFlower(String name, int petals);
}

FlowerService mock = mock(FlowerService.class);

when(mock.isABigFlower("poppy", anyInt())).thenReturn(true);

To fix it and keep the String name “poppy” as it’s desired, we’ll use eq matcher:

when(mock.isABigFlower(eq("poppy"), anyInt())).thenReturn(true);

There are two more points to take care when matchers are used:

  • We can’t use them as a return value, an exact value is required when stubbing calls

  • Finally, we can’t use argument matchers outside of verification or stubbing

In the last case, Mockito will detect the misplaced argument and throw an InvalidUseOfMatchersException.

A bad example could be:

String orMatcher = or(eq("poppy"), endsWith("y"));
verify(mock).analyze(orMatcher);

The way to implement the above code is:

verify(mock).analyze(or(eq("poppy"), endsWith("y")));

Mockito also provides AdditionalMatchers to implement common logical operations (‘not’, ‘and’, ‘or’) on ArgumentMatchers that match both primitive and non-primitive types:

verify(mock).analyze(or(eq("poppy"), endsWith("y")));

4. Custom Argument Matcher

Creating our matcher can be good to select the best possible approach for a given scenario and produce highest quality test, which is clean and maintainable.

For instance, we could have a MessageController that delivers messages. It’ll receive a MessageDTO, and from that, It’ll create a Message to be delivered by MessageService.

Our verification will be simple, verify that we called the MessageService exactly 1 time with any Message:

verify(messageService, times(1)).deliverMessage(any(Message.class));

Because the Message is constructed inside the method under test, we’re forced to use any as the matcher.

This approach doesn’t let us validate the data inside the Message, which can be different compared to the data inside MessageDTO.

For that reason, we’re going to implement a custom argument matcher:

public class MessageMatcher implements ArgumentMatcher<Message> {

    private Message left;

    // constructors

    @Override
    public boolean matches(Message right) {
        return left.getFrom().equals(right.getFrom()) &&
          left.getTo().equals(right.getTo()) &&
          left.getText().equals(right.getText()) &&
          right.getDate() != null &&
          right.getId() != null;
    }
}

To use our matcher, we need to modify our test and replace any by argThat:

verify(messageService, times(1)).deliverMessage(argThat(new MessageMatcher(message)));

Now we know our Message instance will have the same data as our MessageDTO.

5. Custom Argument Matcher vs. ArgumentCaptor

Both techniques custom argument matchers and ArgumentCaptor can be used for making sure certain arguments were passed to mocks.

However, ArgumentCaptor may be a better fit if we need it to assert on argument values to complete verification or our custom argument matcher is not likely to be reused.

Custom argument matchers via ArgumentMatcher are usually better for stubbing.

6. Conclusion

In this article, we’ve explored a feature of Mockito, ArgumentMatcher and its difference with ArgumentCaptor.

As always, the full source code of the examples is available over on GitHub.

Leave a Reply

Your email address will not be published.