Mocks, stubs and spies in unit testing based on Mockito

Tomasz Niegowski

Introduction to mocking

When writing a unit test, there is almost always a need to interact with objects in the code under test. At first glance it seems that the easiest way to manage them is to simply create a new real object and initialize its required fields via a constructor, a builder or setters. But many times it is not possible to do it in a simple way, reasonable time or considering safety. Luckily in unit testing there are commonly known methods to deal with all these types of objects and they will be presented in this article.

Classes presented below will be used in further examples:

Person:

public class Person {

    private String name;
    private String surname;
    private int age;

    public Person(String name, String surname, int age) {
        this.name = name;
        this.surname = surname;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

PersonService:

public class PersonService {
    private PeopleRepository peopleRepository;

    public PersonService(PeopleRepository peopleRepository) {
        this.peopleRepository = peopleRepository;
    }

    List<Person> getAdults() {
        return peopleRepository.getAllPeople().stream()
                .filter(person -> person.getAge() >= 18)
                .collect(Collectors.toList());
    }

    List<Person> findByName(String name) {
        return peopleRepository.getByName(name);
    }
}

Stubs

A stub is an object with sample implementation that imitates the real one, but with a minimum number of methods required for a test. We use it to avoid accessing real data. Stubs always return predefined output, independently from the input. The best use of the stub is when:

  • there is no access to the method returning the data,
  • access to the real object could have side effects (like modification of the data in database).

The best use of stubs is for simple methods. For testing large, complicated objects stubs can be difficult to maintain.

Assuming we want to test PersonService class, but we do not want to operate on real PeopleRepository. In such a situation creating a stub seems to be a good idea:

public class PeopleRepositoryStub implements PeopleRepository{
    @Override
    public List<Person> getAllPeople() {
        Person person1 = new Person("John", "Doe", 35);
        Person person2 = new Person("Joseph", "Dendy", 43);
        return List.of(person1, person2);
    }

    @Override
    public List<Person> getByName(String name) {
        return null;
    }
}

Example test:

@Test
void getAllPeople() {
    //given
    PeopleRepository peopleRepositoryStub = new PeopleRepositoryStub();
    PersonService personService = new PersonService(peopleRepositoryStub);

    //when
    List<Person> people = personService.getAdults();

    //then
    assertThat(people.size(), is(2));
}

Mocks

A mock is an ’empty’ object that simulates a real one. It is not possible to call a method of a mocked object until it is precisely defined what that method should return. Mocks are really useful and popular, as they provide good flexibility and additional features (e.g. the posibilty to verify the number of method calls, call paramaters).

Let’s test the case when the datebase is not going to return any data:

@Test
void getEmptyPeopleList() {
    //given
    PeopleRepository peopleRepository = mock(PeopleRepository.class);
    PersonService personService = new PersonService(peopleRepository);
    given(peopleRepository.getAllPeople()).willReturn(Collections.emptyList());

    //when
    List<Person> people = personService.getAdults();

    //then
    assertThat(people.size(), is(0));
}

We have used Mockito library to create a PeopleRepository mock, passed it as a PersonService constructor parameter and predefined the answer for the getAllPeople method call. As we can see in the provided example, the use of mock is quite easy and elastic.

Spies

A spy is a mixed object of a real one and a mocked one. It behaves like the real object, but the behaviour of specific methods can be mocked. Spies are useful when there is a class with plenty of methods and there is a need of mocking only part of them.

An example test:

@Test
void getPersonNameAndAge() {
    //given
    Person realPerson = new Person("John", "Doe", 35);
    Person spyPerson = spy(realPerson);
    given(spyPerson.getAge()).willReturn(20);

    //when
    String actualName = spyPerson.getName();
    int actualAge = spyPerson.getAge();

    //then
    assertThat(actualName, is("John"));
    assertThat(actualAge, is(20));
}

As we can see, there was created a real Person object and a spy based on the real one. The GetName method was not modified, so we expect the output from the real object, but at the same time we expect modified age. We were able to keep the behaviour of one real method, and change the other ones.

Summary

In the article three types of basic unit test doubles were presented and discussed. Now you should know their characteristics and differences between them, as well as when to use each of them.

Reference

Meet the geek-tastic people, and allow us to amaze you with what it's like to work with j‑labs!

Contact us