Photo by Tata Zaremba on Unsplash

Why Use Mockito At All?

Automatically Generating Mocks and Stubs

Kenneth Kousen
6 min readJan 26, 2023

--

https://pragprog.com/newsletter/

To celebrate the completion of my new book, Mockito Made Clear, I’m writing a series of posts showing some of the features from the framework that I found helpful and interesting. Hopefully, you will find them useful as well.

Testing Hello Dolly

Mockito is a test framework, written in Java, that automatically generates mocks, stubs, and spies. We need to define those terms, but first, why do we need a framework at all? What benefit do you get out of it?

Following a tradition in software development, let’s consider a Hello, World type of problem. Since Mockito is all about representing dependencies of a class you want to test, we need a class that includes dependencies. Let’s call that class HelloMockito, at least for the time being. Its job is to greet a user (say her name is Dolly) by looking them up in persistent storage and translating the greeting (“Hello, Dolly!”) into their language of choice (“हैलो डॉली!”).

import java.util.Optional;

public class HelloMockito {
private String greeting = "Hello, %s, from Mockito!";
private final PersonRepository personRepository;
private final TranslationService translationService;

public HelloMockito(PersonRepository personRepository, TranslationService translationService) {
this.personRepository = personRepository;
this.translationService = translationService;
}

public String greet(int id, String sourceLanguage, String targetLanguage) {
Optional<Person> person = personRepository.findById(id);
String name = person.map(Person::getFirst).orElse("World");
return translationService.translate(String.format(greeting, name), sourceLanguage, targetLanguage);
}
}

To use this class, instantiate it with a constructor that takes both dependent arguments: a person repository for looking up the user by id, and a translation service. The greet method then takes the id and two language codes, one for the source language and one for the target language, and creates the greeting.

There are, therefore, two dependencies that must be supplied. If you were writing a full functional test, you would instantiate the HelloMockito class with both real dependencies, call the greet method, and then check the results. A similar integration test would be like this:

import java.time.LocalDate;

import org.junit.jupiter.api.Test;

import static org.assertj.core.api.Assertions.assertThat;

class HelloMockitoTest {
@Test
@DisplayName("Integration test without mocks")
void helloMockitoWithExplicitStubs() {
// Instantiate a repository implementation and a translation service
// Use them in the HelloMockito constructor
PersonRepository personRepo = new InMemoryPersonRepository();
HelloMockito helloMockito = new HelloMockito(
personRepo,
new DefaultTranslationService()
);

// Save a person in the database
Person person = new Person(1, "Grace", "Hopper",
LocalDate.of(1906, Month.DECEMBER, 9));
personRepo.save(person);

// Greet a user that exists
String greeting = helloMockito.greet(1, "en", "en");
assertThat(greeting).isEqualTo("Hello, Grace, from Mockito!");

// Greet a user that does not exist
greeting = helloMockito.greet(999, "en", "en");
assertThat(greeting).isEqualTo("Hello, World, from Mockito!");
}
}

This test does not include any mocks or stubs. In order to instantiate the HelloMockito class, the test needs to supply implementations of both the PersonRepository interface and the TranslationService class before it can call greet, the actual method under test.

If this test passes, great! But it is rather like testing your car by getting in and driving somewhere. If you arrive at your destination, the car passed the test. In the case of the HelloMockito class, we’ve abstracted a bit away from that, since we have control over the implementations of the dependencies we supply, but we still have to produce them if they don’t already exist.

Downsides of Do-It-Yourself

That way of testing shows some of the downsides of creating your own stand-ins for the dependencies:

  • If the dependency is an interface with several methods, as PersonRespository is here (not shown, it contains more methods than just findById), you need to implement them all even if you need to call only one of them.
  • If you write the stub yourself, you have to maintain it yourself as well.
  • If the dependency isn’t an interface (like TranslationService, also not shown, which is a class), you have to subclass the actual implementation, which may be final or have final methods you can’t override. What do you do then?
  • What if the methods in the dependency are static? You can’t override static methods. You could just edit the source code, but that assumes you have access to it and permission to do so.
  • How about checking failure cases? If you want to see what your class under test does when one of the dependencies fails, you need to create (and maintain) yet another stub to do that.
  • Even with all that, there’s no built-in way in Java to verify that the methods on the dependencies were invoked the right number of times, with the right arguments, in the right order.

Mockito to the Rescue

Here’s where Mockito comes in. Mockito automates the process of creating your own dummy instances of dependencies that behave the way you specify and can track when the class under test calls the methods in those generated instances.

Here is a JUnit 5 test similar to the one given above that uses Mockito to define the behavior of the dependent objects, in all its glory:

import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InOrder;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;

import java.time.LocalDate;
import java.util.Optional;

import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Mockito.*;

// Use the Mockito JUnit 5 extension to wire in the dependencies
@ExtendWith(MockitoExtension.class)
class HelloMockitoWithMocksTest {
@Mock // mocked dependency
private PersonRepository repository;

@Mock // mocked dependency
private TranslationService translationService;

@InjectMocks // inject the mocks into the class under test
private HelloMockito helloMockito;

@Test
@DisplayName("Greet Admiral Hopper")
void greetForPersonThatExists() {
// set expectations on the stubs
when(repository.findById(anyInt()))
.thenReturn(Optional.of(new Person(1, "Grace", "Hopper", LocalDate.now())));
when(translationService.translate("Hello, Grace, from Mockito!", "en", "en"))
.thenReturn("Hello, Grace, from Mockito!");

// test the greet method
String greeting = helloMockito.greet(1, "en", "en");
assertThat(greeting).isEqualTo("Hello, Grace, from Mockito!");

// verify methods were called the proper number of times in the proper order
InOrder inOrder = inOrder(repository, translationService);
inOrder.verify(repository)
.findById(anyInt());
inOrder.verify(translationService)
.translate(anyString(), eq("en"), eq("en"));
}

@Test
@DisplayName("Greet a person not in the database")
void greetForPersonThatDoesNotExist() {
// set expectations on the stubs
when(repository.findById(anyInt()))
.thenReturn(Optional.empty());
when(translationService.translate("Hello, World, from Mockito!", "en", "en"))
.thenReturn("Hello, World, from Mockito!");

// test the greet method
String greeting = helloMockito.greet(100, "en", "en");
assertThat(greeting).isEqualTo("Hello, World, from Mockito!");

InOrder inOrder = inOrder(repository, translationService);
inOrder.verify(repository)
.findById(anyInt());
inOrder.verify(translationService)
.translate(anyString(), eq("en"), eq("en"));
}
}

We’ll examine several parts of these tests (or similar ones) in future posts, but here are the basics:

  1. Mockito creates the mocks or stubs for you. Here that’s done with the @Mock annotation.
  2. You set the expectations on the stubs so their invoked methods do what you want. In these tests, that’s done using the static when method from the Mockito class, followed by thenReturn.
  3. Mockito injects the stubs into the class under test. The Mockito JUnit 5 extension attempts to do that when it encounters the @InjectMocks annotation.
  4. You invoke the method to test (greet) and check the return value (using assertThat from the AssertJ library).
  5. Optionally, you verify that the method under test invoked the methods on the dependencies the right number of times, with the right arguments, in the right order.

Note that it is easy enough to include a failure test as well. We just change the expectations on the repository so it returns an empty Optional, and we’re good to go.

In short, Mockito:

  • Automates the process of generating fake objects for your dependencies
  • Makes it easy to get the objects to do what you want them to do
  • Allows you to verify that the class under test interacted with the objects in the proper way

And those are the benefits of using a testing framework like Mockito.

All source code examples can be found in the GitHub repository for Mockito Made Clear.

Cover of Mockito Made Clear by Ken Kousen featuring a lime and some mint as a play on the ingredients of a mojito
Cover of Mockito Made Clear by Ken Kousen

--

--

Kenneth Kousen
The Pragmatic Programmers

Author of the books Mockito Made Clear, Help Your Boss Help You, Kotlin Cookbook, Modern Java Recipes, Gradle Recipes for Android, and Making Java Groovy