Categories
Java Mockito

Why Use Mocks?

Testing A Simple Publisher/Subscriber System With Mockito

One of the challenges I find when teaching Java testing with Mockito is that the docs, while complete, don’t motivate why you want to use mocks in the first place. This post includes a simple example to show why mocking frameworks are important and where they are useful.

As a quick summary, Mockito is a Java-based framework for creating mocks, stubs, and spies. As described in the famous blog post by Martin Fowler, Mocks Aren’t Stubs, the basic ideas for stubs and mocks are:

  • A stub is a class that stands in for another and returns required outputs given specific inputs.
  • A mock is used to verify the interaction between the class you are testing and a stub. It tests the protocol, meaning you check that the methods on the stub were invoked the right number of times in the right order.

The example is based on one used by the Spock testing framework, shown in the Spock Example project on GitHub. I’ve adapted it for Mockito as an illustration and slightly changed the method names. It does a great job motivating why you need mocks in the first place.

Consider a simple Publisher, which maintains a list of Subscriber instances and sends each them a String message.

public class Publisher {
    private List<Subscriber> subscribers = new ArrayList<>();

    public void subscribe(Subscriber sub) {
        if (!subscribers.contains(sub)) {
            subscribers.add(sub);
        }
    }

    public void send(String message) {
        for (Subscriber sub : subscribers) {
            try {
                sub.onNext(message);
            } catch (Exception ignored) {
                // evil, but what can you do?
            }
        }
    }
}

The Subscriber interface in this simplified model contains only the onNext method:

public interface Subscriber {
    void onNext(String message);
}

(The Publisher class and Subscriber interface are simplified forms of the corresponding types defined by the Reactive Streams specification and included in Java SE 9 and above. The implementations here contain only enough details to highlight why we need mocks.)

Here’s the problem: How do you test the send method in Publisher? It returns void, which is always hard to test. The class has no attributes other than the list of subscribers, so you can’t look for a change of state. Subscribers only have an onNext method which also returns void, so there’s nothing coming back. Worst of all, if the Subscriber throws an exception, the Publisher catches it and ignores it.

The only way to check that the send method is working properly is to verify that the onNext method in each subscriber has been invoked exactly once with the proper argument. That’s what mocks do.

(Note that the following tests use JUnit 5.)

The PublisherTest starts by initializing a Publisher and adding two mock Subscriber instances:

public class PublisherTest {

    private final Publisher pub = new Publisher();
    private final Subscriber sub1 = mock(Subscriber.class);
    private final Subscriber sub2 = mock(Subscriber.class);

    @BeforeEach
    public void setUp() {
        pub.subscribe(sub1);
        pub.subscribe(sub2);
    }

    // ... tests to come ...
}

In this particular case, I don’t need to set any expectations on the stubs, because the onNext method doesn’t return anything. I just need to make sure the method is called exactly once on each subscriber with the proper argument. That’s done with a simple verify:

@Test
public void testSend() {
    pub.send("Hello");

    verify(sub1).onNext("Hello");
    verify(sub2).onNext("Hello");
}

The verify method checks that onNext was invoked on each subscriber with the String argument Hello.

If we assume that one of the subscribers fails and throws an exception every time, the awful “catch and ignore” behavior of the send method can be verified as well:

@Test
public void testSendWithBadSubscriber() {
    // sub1 throws an exception every time
    doThrow(RuntimeException.class).when(sub1).onNext(anyString());

    pub.send("message 1");
    pub.send("message 2");

    // sub2 still receives the messages
    verify(sub2, times(2))
            .onNext(argThat(s -> s.matches("message \\d")));
}

In this case, the doThrow method is used to make the first subscriber throw a runtime exception whenever its onNext method is invoked with any string, using the built-in argument matcher anyString.

The verify method this time checks that the onNext method was invoked twice on the second subscriber. The argument employs a custom matcher. Mockito uses the argThat method to let you provide a custom argument matcher, which must implement the ArgumentMatcher interface. That interface has only a single abstract method:

public interface ArgumentMatcher<T> {
    boolean matches(T argument);
}

The ArgumentMatcher is thus a functional interface, so its implementation can be provided with a lambda expression. In the test above, the custom matcher checks that the argument consists of the word "message" followed by a space and a digit.

The testSendWithBadSubscriber method only checks that the second subscriber received the proper message. In principle you could add assertions to verify that the first subscriber threw the exception. This is particularly easy to do with JUnit 5, which includes an assertThrows method (and an assertAll method to ensure both onNext invocations are checked even if one fails):

@Test
public void testSendWithBadSubscriber() {
    // ... after setting expectation and invoking pub.send(...) ...

    assertAll(
            () -> assertThrows(RuntimeException.class,
                    () -> sub1.onNext("message 1")),
            () -> assertThrows(RuntimeException.class,
                    () -> sub1.onNext("message 2")));

    // ... verify from before ...
}

It’s not actually necessary, however, to check that the stubs do what you tell them to do when their expectations were set earlier in the same method. Still, it doesn’t hurt anything either.

The tests check that the publisher’s send method is behaving as designed, through the behavior of the two subscribers. Honestly, it’s hard to think of any other way to do that. Hopefully this gives you a sense of when mocks can be useful.

All the code, along with many more examples I use in my training course on Mockito and the Hamcrest Matchers, is contained this GitHub repository.