Creating Stubs With Mockito

After we have finished this lesson, we

  • Understand how we can highlight the fact that the created test double is a stub.
  • Can configure a stub to return an object or throw an exception when its method is invoked.
  • Know how we can configure the expected method parameters by using argument matchers.

Highlighting the Type of a Stub

As we remember, Mockito doesn’t differentiate dummies, stubs, and mocks. The Mockito class provides one method that can be used for creating dummies, stubs, and mocks. This method is called mock(), and every test double that is created by using this method can be a mock.

Earlier we created an object mother class that provides a factory method called dummy(). We also learned that highlighting the type of the created test double helps us to clarify our intentions and reduce misunderstandings.

That is why we will add a factory method called stub() into our object mother class. This method takes a Class<T> object as a method parameter, passes its method parameter forward to the mock() method of the Mockito class, and returns the created test double. After we have added this method into the TestDoubles class, its source code looks as follows:

 1import static org.mockito.Mockito.mock;
 2
 3public final class TestDoubles {
 4
 5    private TestDoubles() {}
 6
 7    public static <T> T dummy(Class<T> dummyClass) {
 8        return mock(dummyClass);
 9    }
10
11    public static <T> T stub(Class<T> stubClass) {
12        return mock(stubClass);
13    }
14}

We can now create stubs without invoking the mock() method in our test class. Before we will get our hands dirty, we have to take a quick look at the stubbed interface.

Introduction to System Under Test

The system under test has a single dependency called TaskRepository. This interface declares four methods that are described in the following:

  • The count() method returns the number of tasks that are found from the database.
  • The delete() method deletes the task that is given as a method parameter. This method doesn’t return anything and it throws a NotFoundException if the deleted task is not found from the database.
  • The findById() method finds the task whose id is given as a method parameter and returns an Optional object that contains the found task. If no task is found, this method returns an empty Optional object.
  • The save() method saves the task given as a method parameter to the database and returns the saved task.

The source code of the TaskRepository interface looks as follows:

 1import java.util.Optional;
 2
 3interface TaskRepository {
 4
 5    long count();
 6
 7    void delete(Task task);
 8
 9    Optional<Task> findById(Long id);
10
11    Task save(Task task);
12}

Let’s move on and find out how we can stub method behavior with Mockito.

Stubbing Method Behavior With Mockito

The first thing that we have to do is to create our stub. Because the test doubles that are created with Mockito are not stateless, we have to create a new test double before a test method is invoked. The easiest way to do this is create our stub in setup method that is annotated with the @Before annotation.

After we have added this setup method into our test class and created a TaskRepository stub by invoking the stub() method of the TestDoubles class, the source code of our test class looks as follows:

 1import org.junit.Before;
 2
 3import static com.testwithspring.task.TestDoubles.stub;
 4
 5public class BDDMockitoTest {
 6
 7    private TaskRepository repository;
 8
 9    @Before
10    public void createStub() {
11        repository = stub(TaskRepository.class);
12    }
13}

Let’s get our hands dirty and stub some methods with Mockito.

Returning Values and Objects Without Method Parameters

If we want to configure the returned value or object by using BDDMockito when the stubbed method doesn’t have method parameters, we have to follow these steps:

  1. Configure the stubbed method by invoking the given() method of the BDDMockito class. When we invoke this method, we have to pass the stubbed method call as a method parameter.
  2. Configure the returned value or object by invoking the willReturn() method of the BDDMyOngoingStubbing<T> interface. When we invoke this method, we have to pass the returned value or object as a method parameter.

After we have configured the count() method of the TaskRepository interface to return 1L when it is invoked, the source code of our test method looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3
 4import static com.testwithspring.task.TestDoubles.stub;
 5import static org.assertj.core.api.Assertions.assertThat;
 6import static org.mockito.BDDMockito.given;
 7
 8public class BDDMockitoTest {
 9
10    private TaskRepository repository;
11
12    @Before
13    public void createStub() {
14        repository = stub(TaskRepository.class);
15    }
16
17    @Test
18    public void shouldReturnValue() {
19        given(repository.count()).willReturn(1L);
20
21        long actualCount = repository.count();
22
23        assertThat(actualCount).isEqualByComparingTo(1L);
24    }
25}

If we want to use the original Mockito API, we have to follow the same steps but invoke different methods. We can configure the stubbed method by invoking the when() method of the Mockito class and configure the returned value by invoking the thenReturn() method of the OngoingStubbing<T> interface.

The code that uses the original Mockito API looks as follows:

1when(repository.count()).thenReturn(1L);

Returning Values and Objects With Method Parameters

If we want to configure the returned value or object by using BDDMockito when the stubbed method has method parameters, we have to follow these steps:

  1. Configure the stubbed method by invoking the given() method of the BDDMockito class. When we invoke this method, we have to pass the stubbed method call as a method parameter. When we create the stubbed method call, we have to configure the expected method parameter.
  2. Configure the returned value or object by invoking the willReturn() method of the BDDMyOngoingStubbing<T> interface. When we invoke this method, we have to pass the returned value or object as a method parameter.

After we have configured the findById() method of the TaskRepository interface to return a non-empty Optional object when it is invoked by using the method parameter 1L, the source code of our test method looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3
 4import java.util.Optional;
 5
 6import static com.testwithspring.task.TestDoubles.dummy;
 7import static com.testwithspring.task.TestDoubles.stub;
 8import static org.assertj.core.api.Assertions.assertThat;
 9import static org.mockito.BDDMockito.given;
10
11public class BDDMockitoTest {
12
13    private TaskRepository repository;
14
15    @Before
16    public void createStub() {
17        repository = stub(TaskRepository.class);
18    }
19
20    @Test
21    public void shouldReturnObject() {
22        Task expected = dummy(Task.class);
23        given(repository.findById(1L)).willReturn(Optional.of(expected));
24
25        Optional<Task> actual = repository.findById(1L);
26
27        assertThat(actual).containsSame(expected);
28    }
29}

If we want to use the original Mockito API, we have to follow the same steps but invoke different methods. We can configure the stubbed method by invoking the when() method of the Mockito class and configure the returned value by invoking the thenReturn() method of the OngoingStubbing<T> interface.

The code that uses the original Mockito API looks as follows:

1Task expected = dummy(Task.class);
2when(repository.findById(1L)).thenReturn(Optional.of(expected));

Using Argument Matchers

If we configure the expected method parameter by simply passing it to the stubbed method call, Mockito verifies it by using the equals() method. Even though this is fine for most situations, sometimes we might need extra flexibility. When this happens, we can specify the expected method parameter by using an argument matcher that is passed to the stubbed method call as a method parameter.

The org.mockito.Matchers class provides factory methods that we can use for creating different argument matchers. Let’s take a look at two examples that demonstrate how argument matchers work.

Additional Reading:

First, if we want to configure the findById() method of the TaskRepository interface to return a non-empty Optional object when it is invoked by using a method parameter that is equal to 1L, we have to create our argument matcher by using the eq() method of the Matchers class.

After we have configured the expected method parameter by using the created argument matcher, the source code of our test method looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3
 4import java.util.Optional;
 5
 6import static com.testwithspring.task.TestDoubles.dummy;
 7import static com.testwithspring.task.TestDoubles.stub;
 8import static org.assertj.core.api.Assertions.assertThat;
 9import static org.mockito.BDDMockito.given;
10import static org.mockito.Matchers.eq;
11
12public class BDDMockitoTest {
13
14    private TaskRepository repository;
15
16    @Before
17    public void createStub() {
18        repository = stub(TaskRepository.class);
19    }
20
21    @Test
22    public void shouldReturnObjectWhenExpectedIsEqualToActual() {
23        Task expected = dummy(Task.class);
24        given(repository.findById(eq(1L))).willReturn(Optional.of(expected));
25
26        Optional<Task> actual = repository.findById(1L);
27
28        assertThat(actual).containsSame(expected);
29    }
30}

If we want to use the original Mockito API, we have to configure our stub by using the following code:

1Task expected = dummy(Task.class);
2when(repository.findById(eq(1L))).thenReturn(Optional.of(expected));

Second, if we want to configure the findById() method of the TaskRepository interface to return a non-empty Optional object when it is invoked and any Long object is passed as a method parameter, we have to create our argument matcher by using the anyLong() method of the Matchers class.

After we have configured the expected method parameter by using the created argument matcher, the source code of our test method looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3
 4import java.util.Optional;
 5
 6import static com.testwithspring.task.TestDoubles.dummy;
 7import static com.testwithspring.task.TestDoubles.stub;
 8import static org.assertj.core.api.Assertions.assertThat;
 9import static org.mockito.BDDMockito.given;
10import static org.mockito.Matchers.anyLong;
11
12public class BDDMockitoTest {
13
14    private TaskRepository repository;
15
16    @Before
17    public void createStub() {
18        repository = stub(TaskRepository.class);
19    }
20
21    @Test
22    public void shouldReturnObjectWhenParameterIsAnyLong() {
23        Task expected = dummy(Task.class);
24        given(repository.findById(anyLong())).willReturn(Optional.of(expected));
25
26        Optional<Task> actual = repository.findById(1L);
27
28        assertThat(actual).containsSame(expected);
29    }
30}

If we want to use the original Mockito API, we have to configure our stub by using the following code:

1Task expected = dummy(Task.class);
2when(repository.findById(anyLong())).thenReturn(Optional.of(expected));
If the stubbed method has multiple method parameters and we want to use argument matchers, we have to provide all method parameters by using argument matchers.

Throwing Exceptions

If we want to configure the exception that is thrown when a stubbed method is invoked, we can use one of these two techniques:

First, if the stubbed method returns something (i.e. its return type is not void), we have to follow these steps:

  1. Configure the stubbed method by invoking the given() method of the BDDMockito class. When we invoke this method, we have to pass the stubbed method call as a method parameter.
  2. Configure the thrown exception by invoking the willThrow() method of the BDDMyOngoingStubbing<T> interface. When we invoke this method, we have to pass the exception as a method parameter.

After we have configured the findById() method to throw a NotFoundException when its invoked by using the method parameter 1L, the source code of our test method looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3
 4import static com.testwithspring.task.TestDoubles.stub;
 5import static org.assertj.core.api.Assertions.assertThat;
 6import static org.mockito.BDDMockito.given;
 7
 8public class BDDMockitoTest {
 9
10    private TaskRepository repository;
11
12    @Before
13    public void createStub() {
14        repository = stub(TaskRepository.class);
15    }
16
17    @Test(expected = NotFoundException.class)
18    public void shouldThrowExceptionWhenMethodHasReturnValue() {
19        given(repository.findById(1L)).willThrow(new NotFoundException());
20
21        repository.findById(1L);
22    }
23}

If we want to use the original Mockito API, we have to follow the same steps but invoke different methods. We can configure the stubbed method by invoking the when() method of the Mockito class and configure the thrown exception by invoking the thenThrow() method of the OngoingStubbing<T> interface.

The code that uses the original Mockito API looks as follows:

1when(repository.findById(1L)).thenThrow(new NotFoundException());

This method cannot throw an exception. If the requested task is not found, this method returns an empty Optional.

I use this example here because I wanted to demonstrate how we can throw an exception if the stubbed method returns something. That being said, we must not break the API contract of our API when we stub method behavior!

Second, if the stubbed method doesn’t return anything (i.e. its return type is void), we have to follow these steps:

  1. Configure the thrown exception by invoking the willThrow() method of the BDDMockito class. When we invoke this method, we have to pass the thrown exception as a method parameter.
  2. Configure the target stub by invoking the given() method of the BDDStubber interface. When we invoke this method, we naturally have to pass the stub object as a method parameter.
  3. Configure the stubbed method by invoking it. When we invoke the stubbed method, we have to configure the expected method parameters.

After we have configured the delete() method to throw a NotFoundException, the source code of our test method looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3
 4import static com.testwithspring.task.TestDoubles.dummy;
 5import static com.testwithspring.task.TestDoubles.stub;
 6import static org.assertj.core.api.Assertions.assertThat;
 7import static org.mockito.BDDMockito.given;
 8import static org.mockito.BDDMockito.willThrow;
 9
10public class BDDMockitoTest {
11
12    private TaskRepository repository;
13
14    @Before
15    public void createStub() {
16        repository = stub(TaskRepository.class);
17    }
18
19    @Test(expected = NotFoundException.class)
20    public void shouldThrowExceptionWhenMethodHasNoReturnValue() {
21        Task deleted = dummy(Task.class);
22        willThrow(new NotFoundException()).given(repository).delete(deleted);
23
24        repository.delete(deleted);
25    }
26}

If we want to use the original Mockito API, we have to follow the same steps but invoke different methods. We can configure the thrown exception by invoking the doThrow() method of the Mockito class and configured the stub object by invoking the when() method of the Stubber interface.

The code that uses the original Mockito API looks as follows:

1Task deleted = dummy(Task.class);
2doThrow(new NotFoundException()).when(repository).delete(deleted);

Configuring an Answer

Even though configuring the behavior of a stubbed method by using the “standard” factory methods is suitable for most use cases, sometimes these factory methods are too restrictive.

For example, if we want to access the actual object that is passed to the stubbed method as a method parameter, we cannot use argument matchers. However, we can solve this problem by creating an Answer<T> object that specifies the behavior of the stubbed method. When we create a new Answer object we have to specify one type parameter (T) that specifies the type of the returned object.

If we want create an Answer<Optional<Task>> object which ensures that the findById() method returns a non-empty Optional object when it is invoked by using the method parameter 1L, we have to follow these steps:

  1. Configure the stubbed method by invoking the given() method of the BDDMockito class. When we invoke this method, we have to pass the stubbed method call as a method parameter. When we create the stubbed method call, we have to pass 1L as a method parameter.
  2. Configure the Answer that specifies the behavior of the stubbed method by invoking the willAnswer() method of the BDDMyOngoingStubbing<T> interface.
  3. Create a new Answer<Optional<Task>> object and pass it to the willAnswer() method as a method parameter.
  4. Implement the answer() method of the Answer class by returning a non-empty Optional object if the actual method parameter is equal to 1L.

After we configured the behavior of the findById() method by using an Answer<Optional<Task>> object, the source code of the test method looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.mockito.invocation.InvocationOnMock;
 4import org.mockito.stubbing.Answer;
 5
 6import java.util.Optional;
 7
 8import static com.testwithspring.task.TestDoubles.dummy;
 9import static com.testwithspring.task.TestDoubles.stub;
10import static org.assertj.core.api.Assertions.assertThat;
11import static org.mockito.BDDMockito.given;
12
13public class BDDMockitoTest {
14
15    private TaskRepository repository;
16
17    @Before
18    public void createStub() {
19        repository = stub(TaskRepository.class);
20    }
21
22    @Test
23    public void shouldReturnObjectByUsingAnswer() {
24        Task expected = dummy(Task.class);
25        given(repository.findById(1L)).willAnswer(new Answer<Optional<Task>>() {
26            @Override
27            public Optional<Task> answer(InvocationOnMock invocation) throws Throwable {
28                Long idParameter = (Long) invocation.getArguments()[0];
29                if (idParameter.equals(1L)) {
30                    return Optional.of(expected);
31                }
32                else {
33                    return Optional.empty();
34                }
35            }
36        });
37
38        Optional<Task> actual = repository.findById(1L);
39
40        assertThat(actual).containsSame(expected);
41    }
42}

If we want to use the original Mockito API, we have to follow the same steps but invoke different methods. We can configure the stubbed method by invoking the when() method of the Mockito class and configure the behavior of the stubbed method by invoking the thenAnswer() method of the OngoingStubbing<T> interface.

The code that uses the original Mockito API looks as follows:

 1Task expected = dummy(Task.class);
 2when(repository.findById(anyLong())).thenAnswer(new Answer<Optional<Task>>() {
 3	@Override
 4	public Optional<Task> answer(InvocationOnMock invocation) throws Throwable {
 5		Long idParameter = (Long) invocation.getArguments()[0];
 6		if (idParameter.equals(1L)) {
 7			return Optional.of(expected);
 8		}
 9		else {
10			return Optional.empty();
11		}
12	}
13});

We can now configure the behavior of our stub by using Mockito. Let’s put our new skills into practice.

Putting Theory Into Practice

Let’s use our new skills and write one unit test for the createCopyOf() method of the TaskService class. This method was implemented by following these steps:

  1. Verify that both method parameter are not null.
  2. Find the source task from the database.
  3. If the source task is not found, throw a NotFoundException.
  4. Create a new Task object. Copy the title and description from the source task and obtain the id of the creator from the LoggedInUser object given as a method parameter.
  5. Save the created Task object in the database.
  6. Return the saved Task object.

The source code of the TaskService class looks as follows:

 1import com.google.common.base.Preconditions;
 2import com.testwithspring.user.LoggedInUser;
 3import org.slf4j.Logger;
 4import org.slf4j.LoggerFactory;
 5
 6import java.util.Optional;
 7
 8public class TaskService {
 9
10    private static final Logger LOGGER = LoggerFactory
11			.getLogger(TaskService.class);
12
13    private final TaskRepository repository;
14
15    public TaskService(TaskRepository repository) {
16        this.repository = repository;
17    }
18
19    public Task createCopyOf(Long sourceTaskId, LoggedInUser creator) {
20        Preconditions.checkNotNull(sourceTaskId);
21        Preconditions.checkNotNull(creator);
22
23        LOGGER.info("Creating a copy of the task: {} with creator: {}",
24                sourceTaskId,
25                creator.getUsername()
26        );
27
28        Optional<Task> found = repository.findById(sourceTaskId);
29        Task source = found.orElseThrow(NotFoundException::new);
30
31        Task created = Task.getBuilder()
32                .withCreator(creator.getUserId())
33                .withDescription(source.getDescription())
34                .withTitle(source.getTitle())
35                .build();
36        created = repository.save(created);
37
38        LOGGER.info("Created a new task: {}", created);
39
40        return created;
41    }
42}

We have to write a unit test which ensures that the createCopyOf() method returns the information of the created task when the source task is found from the database. Because the goal of this lesson is to teach us how we can stub method behavior, the other parts of this unit test have been implemented by unknown developer. The only thing that we have to do is to configure the TaskRepository stub.

We can do this by following these steps:

  1. Configure the TaskRepository stub to return the source task when its findById() method is invoked by using the correct id.
  2. Configure the TaskRepository stub to return the saved Task when its save() method is invoked and any Task object is passed as a method parameter.

After we have configured the TaskRepository stub, the source code of our test class looks as follows:

 1import org.junit.Before;
 2import org.junit.Test;
 3import org.mockito.invocation.InvocationOnMock;
 4import org.mockito.stubbing.Answer;
 5
 6import java.util.Optional;
 7
 8import static com.testwithspring.task.TestDoubles.dummy;
 9import static com.testwithspring.task.TestDoubles.stub;
10import static org.assertj.core.api.Assertions.assertThat;
11import static org.mockito.BDDMockito.given;
12import static org.mockito.Matchers.isA;
13
14public class BDDMockitoTaskServiceTest {
15
16    private final Long CREATOR_ID = 33L;
17    private final String CREATOR_USERNAME = "username";
18    private final Long NEW_TASK_ID = 120L;
19    private final Long SOURCE_TASK_ID = 99L;
20    private final String SOURCE_TASK_DESCRIPTION = "description";
21    private final String SOURCE_TASK_TITLE = "title";
22
23    private TaskRepository repository;
24    private TaskService service;
25
26    @Before
27    public void configureSystemUnderTest() {
28        repository = stub(TaskRepository.class);
29        service = new TaskService(repository);
30    }
31
32    @Test
33    public void createCopyOf_WhenSourceTaskIsFound_ShouldReturnCreatedTask() {
34        Task sourceTask = new TaskBuilder()
35                .withDescription(SOURCE_TASK_DESCRIPTION)
36                .withTitle(SOURCE_TASK_TITLE)
37                .build();
38
39        given(repository.findById(SOURCE_TASK_ID))
40				.willReturn(Optional.of(sourceTask));
41        given(repository.save(isA(Task.class))).willAnswer(new Answer<Task>() {
42            @Override
43            public Task answer(InvocationOnMock invocation) throws Throwable {
44                Task parameter = (Task) invocation.getArguments()[0];
45                return new TaskBuilder()
46                        .withId(NEW_TASK_ID)
47                        .withCreator(parameter.getCreator().getUserId())
48                        .withDescription(parameter.getDescription())
49                        .withTitle(parameter.getTitle())
50                        .build();
51            }
52        });
53
54        LoggedInUser creator = new LoggedInUser(CREATOR_ID, CREATOR_USERNAME);
55        Task created = service.createCopyOf(SOURCE_TASK_ID, creator);
56
57        assertThat(created.getId()).isEqualByComparingTo(NEW_TASK_ID);
58        assertThat(created.getCreator().getUserId())
59				.isEqualByComparingTo(CREATOR_ID);
60        assertThat(created.getDescription())
61				.isEqualTo(SOURCE_TASK_DESCRIPTION);
62        assertThat(created.getTitle()).isEqualTo(SOURCE_TASK_TITLE);
63    }
64}

This example demonstrates why we have to configure the behavior of a stubbed method by using the Answer<T> class. Because we want copy the returned information from the Task object that was given to the save() method as a method parameter, we need to access the actual method parameter. The only way we can access the actual method parameter is to use the Answer<T> class.

Let’s summarize what we learned from this lesson.

Summary

This lesson has taught us five things:

  • We have to create our stub before a test method is invoked.
  • We can configure the behavior of our stub by using the factory methods provided by the BDDMockito and Mockito classes.
  • By default, Mockito compares the expected method parameters with the actual method parameters by using the equals() method.
  • We can customize the method parameter verification process by using argument matchers.
  • If we want to access the actual method parameter when we configure the behavior of a stubbed method, we have to use the Answer<T> class.

Additional Reading:

Get the source code from Github