1. Overview
In this tutorial, we'll explore different ways to return custom values from mocked methods. For this purpose, Mockito provides the Answer interface. Throughout the article, we'll cover different use cases and different Answer implementations.
2. The Answer Interface
Let's start by investigating the Answer interface.
The Answer interface enables us to define custom return values and has the following contract:
public interface Answer<T> {
/**
* @param invocation the invocation on the mock.
*
* @return the value to be returned
*
* @throws Throwable the throwable to be thrown
*/
T answer(InvocationOnMock invocation) throws Throwable;
}
It has the answer() method with the invocation parameter which provides us runtime related information. Moreover, it returns a value parameterized as T.
Next, let's see how we can use the Answer implementations.
When we're defining return values on mocked methods, we generally use when().thenReturn() statements. However, for Answer implementations, we must use the when().then() statement:
Mockito.when(mockCall()).then(new customAnswer())
3. Accessing the Method Arguments using Answer
Now, we'll look at how we can access the method arguments in an Answer implementation.
But before that, let's see our sample classes. We have the PersonRepository and PersonService classes. Also, note that PersonService includes PersonRepository as a dependency.
public class PersonService {
private final PersonRepository personRepository;
public PersonService(PersonRepository personRepository) {
this.personRepository = personRepository;
}
...
public Person select(Person first, Person second, Person third) {
return personRepository.select(first, second, third);
}
}
public class PersonRepository {
...
public Person select(Person first, Person second, Person third) {
return first;
}
}
Here, in the select methods, we're declaring three Person parameters. Our Answer implementation will return the first one:
@InjectMocks
private PersonService personService;
@Mock
private PersonRepository personRepository;
@Test
public void shouldReturnFirstPerson() {
Person firstPerson = new Person("first");
Person secondPerson = new Person("second");
Person thirdPerson = new Person("third");
Mockito.when(personRepository.select(firstPerson, secondPerson, thirdPerson))
.thenAnswer(new Answer<Person>() {
@Override
public Person answer(InvocationOnMock invocation) throws Throwable {
return invocation.getArgumentAt(0, Person.class);
}
});
Person actual = personService.select(firstPerson, secondPerson, thirdPerson);
Assertions.assertThat(actual).isEqualTo(firstPerson);
}
In the Answer implementation, we're getting the first argument and casting it to Person - invocation.getArgumentAt(0, Person.class). Then we're returning this Person instance, whenever PersonService.select is called.
4. Calling the Real Method using Answer
Alternatively, we can also call the real method in an Answer implementation:
@Test
public void shouldCallRealMethod() {
Person firstPerson = new Person("first");
Person secondPerson = new Person("second");
Person thirdPerson = new Person("third");
Person other = new Person("other");
Mockito.when(personRepository.select(firstPerson, secondPerson, thirdPerson))
.thenAnswer(new Answer<Person>() {
@Override
public Person answer(InvocationOnMock invocation) throws Throwable {
return (Person) invocation.callRealMethod();
}
});
Person actual = personService.select(firstPerson, secondPerson, thirdPerson);
Assertions.assertThat(actual).isEqualTo(firstPerson);
}
Here, our Answer implementation calls the real method - PersonRepository.select - and returns firstPerson.
5. Summary
In this tutorial, we investigated how we can return custom values on mocked methods using the Answer interface.
As always, the source code is available on Github.