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.
This lesson assumes that:
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 aNotFoundExceptionif 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 anOptionalobject that contains the found task. If no task is found, this method returns an emptyOptionalobject. - 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:
- Configure the stubbed method by invoking the
given()method of theBDDMockitoclass. When we invoke this method, we have to pass the stubbed method call as a method parameter. - Configure the returned value or object by invoking the
willReturn()method of theBDDMyOngoingStubbing<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:
- Configure the stubbed method by invoking the
given()method of theBDDMockitoclass. 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. - Configure the returned value or object by invoking the
willReturn()method of theBDDMyOngoingStubbing<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));
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:
- Configure the stubbed method by invoking the
given()method of theBDDMockitoclass. When we invoke this method, we have to pass the stubbed method call as a method parameter. - Configure the thrown exception by invoking the
willThrow()method of theBDDMyOngoingStubbing<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:
- Configure the thrown exception by invoking the
willThrow()method of theBDDMockitoclass. When we invoke this method, we have to pass the thrown exception as a method parameter. - Configure the target stub by invoking the
given()method of theBDDStubberinterface. When we invoke this method, we naturally have to pass the stub object as a method parameter. - 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:
- Configure the stubbed method by invoking the
given()method of theBDDMockitoclass. 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 pass1Las a method parameter. - Configure the
Answerthat specifies the behavior of the stubbed method by invoking thewillAnswer()method of theBDDMyOngoingStubbing<T>interface. - Create a new
Answer<Optional<Task>>object and pass it to thewillAnswer()method as a method parameter. - Implement the
answer()method of theAnswerclass by returning a non-emptyOptionalobject if the actual method parameter is equal to1L.
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:
- Verify that both method parameter are not
null. - Find the source task from the database.
- If the source task is not found, throw a
NotFoundException. - Create a new
Taskobject. Copy the title and description from the source task and obtain the id of the creator from theLoggedInUserobject given as a method parameter. - Save the created
Taskobject in the database. - Return the saved
Taskobject.
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:
- Configure the
TaskRepositorystub to return the source task when itsfindById()method is invoked by using the correct id. - Configure the
TaskRepositorystub to return the savedTaskwhen itssave()method is invoked and anyTaskobject 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
BDDMockitoandMockitoclasses. - 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: