Mockito¶
Introduction¶
Mockito
is a library that provides an API for creating the so-called mocks. Mocking is nothing else than creating a dummy which is an implementation of a given object. Such a dummy can be modeled freely. This way specific behaviors of objects are programmed without going into the implementation details. An example use of mocking can be observed when a user service class uses a database. If you want to test the service class, you should also provide a database configuration, although it should not be tested. In this case, it is much more effective to prepare the so-called mock which will only model the behavior of the database, without actually implementing its functionality.
Integration¶
In order to start working with mockito
, the following dependencies should be included in an existing project:
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-junit-jupiter</artifactId>
<version>3.3.3</version>
<scope>test</scope>
</dependency>
Ways of integration¶
We can use Mockito in two ways. In order to create mocks and inject them, we can choose:
- the declarative way, i.e. using annotations
- the programmatic way, i.e. using appropriate methods.
When we choose the declarative way, JUnit "understands" and can handle additional annotations. You need to add the extension @ExtendWith (MockitoExtension.class)
, responsible for integrating the Mockito API within the implemented unit tests.
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
//...
}
Creating mocks¶
We can create Mocks in two ways:
- with the
Mockito.mock (type)
method - with the
@ Mock
annotation.
Suppose we want to create unit tests for the class UserService
with the following definition:
public class UserService {
private final UserValidator userValidator;
private final UserRepository userRepository;
public UserService(final UserValidator userValidator, final UserRepository userRepository) {
this.userValidator = userValidator;
this.userRepository = userRepository;
}
}
The UserService
class depends on the UserRepository
and UserValidator
classes. Hence we should mock these dependencies:
@ExtendWith(MockitoExtension.class) // required because we're using the mockito annotations
class UserServiceTest {
@Mock
private UserValidator userValidator; // or alternatively Mockito.mock(UserValidator.class);
@Mock
private UserRepository userRepository; // or alternatively Mockito.mock(UserRepository.class);
private UserService service;
@BeforeEach
void setUp() {
service = new UserService(userValidator, userRepository);
}
}
A less frequently used method is, in turn, creating and injecting a mock into a test method, e.g.:
@Test
void shouldInjectMock(@Mock UserRepository userRepository) {
System.out.println("I have injected a mock to a test method");
}
NOTE: We can use the
@Mock
annotation on both classes and interfaces.
@InjectMocks¶
In the previous example, we initialized the tested object in the test lifecycle method (@BeforeEach
) by calling the available constructor. The UserService
class has only two dependencies, but in reality classes sometimes may incorporate way more. In this case, invoking a multi-argument constructor with prepared mocks can be inconvenient.
The mockito mechanism which solves this problem is the annotation @InjectMocks
. It automatically creates an object using available mocks. We use this annotation over the class field declaration. The previous example can be simplified as follows:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserValidator userValidator;
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService service;
}
Modeling mock behavior¶
Having created mocks, we can begin to model their behavior. This means that we can define, for example:
- the object that will be returned when calling the method
- how the next call of a specific method will behave
- what exception will be thrown when calling a given method.
Method when¶
In order to indicate the method whose behavior we model, we should use its call as an argument to the static method when
. This method comes from the Mockito
class.
For example, for the RandomIntGenerator
class:
public class RandomIntGenerator {
public int generate() {
return new Random().nextInt();
}
}
indicating the "mock" behavior of the generate
method would look as follows:
@ExtendWith(MockitoExtension.class)
class RandomIntGeneratorMockitoTest {
@Mock
private RandomIntGenerator randomIntGenerator;
@Test
void shouldDemoHowWhenFromMockitoWorks() {
when(randomIntGenerator.generate()); // when from Mockito class
}
}
then method group¶
When we indicate which method and behavior we want to program, after calling the when
method, we need to call one of the methods from thethen
group:
then
thenReturn
thenAnswer
thenThrow
.
thenReturn¶
The thenReturn
method is the most common version of thethen
method group. Its argument is an object that will be returned when the method is called from the when
part.
To follow up on the previous example, we need to "mock" the generate
method so that its call returns the value5
. The result will look like this:
when(randomIntGenerator.generate()).thenReturn(5);
When modeling the behavior of a method that has input arguments, we must provide specific values. On this basis the specific behavior will be triggered.
For example, for the User
andUserValidator
classes:
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String firstName;
private String lastName;
}
public class UserValidator {
public boolean isUserValid(final User user) {
return !user.getFirstName().isBlank() && !user.getLastName().isBlank();
}
}
mocking the method isUserValid
on userValidator
mock can look as follows:
@Test
void shouldJustShowHowToMockMethodWithArgument() {
final User userA = new User(1L, "Andrew", "");
final User userB = new User(2L, "George", "Johnson");
when(userValidator.isUserValid(userA)).thenReturn(true); // (1)
when(userValidator.isUserValid(userB)).thenReturn(false); // (2)
System.out.println(userValidator.isUserValid(userA)); // (3)
System.out.println(userValidator.isUserValid(userB)); // (4)
System.out.println(userValidator.isUserValid(new User())); // (5)
}
In the example above, in lines (1)
and (2)
we "mock" the behavior of the calls to the method isUserValid
for very specific arguments. According to our behavior, calling this method on a mock will return true
on line(3)
(despite the fact that the real object would returnfalse
), and on line (4 )
it will return false. Additionally, in line
(5), the value
falsewill also be displayed, even though we did not define this behavior. By default, mocks that have been used in some way, but which we did not anticipate, will return "empty" values. For isntance, "false" for
booleanor an empty list for the
Listobject. We can override this behavior by using the
answerattribute on the
@Mock` annotation.
thenThrow¶
To test the negative scenario and make sure that our code correctly handles exceptions, which can throw out dependent classes, we can use the thenThrow
method, which causes an exception to be thrown when the method is called in thewhen
section, e.g.:
public class UserRepository {
public User getById(final Long id) {
// method implementation that returns a user OR throws NoSuchElementException
}
}
@Test
void shouldJustShowHowToMockMethodToThrowException() {
final Long id = 1L;
when(userRepository.getById(id)).thenThrow(new NoSuchElementException("User with given id does not exist"));
assertThatExceptionOfType(NoSuchElementException.class).isThrownBy(() -> userRepository.getById(id)); // ok, the exception occured
assertThatExceptionOfType(NoSuchElementException.class).isThrownBy(() -> userRepository.getById(2L)); // error, the exception didn't occur
}
Verifications¶
When modeling the behavior of mounted objects, we want to be sure that the behaviors we have prepared will be used in the test. In order to verify this fact, the Mockito
class provides the following static methods:
verify
verifyNoInteractions
verifyNoMoreInteractions
verify¶
The verify
method allows you to verify whether there has been any interaction with the mock passed as a parameter to the method. The argument to the verify
method is the mock we are currently investigating. The verify
method additionally returns an object that has the same interface as our mock. We call the expected method with its arguments after the dot. If no such interaction occurs in the test, the test will fail.
verify(someMock).methodFromMocksAPI("argsList");
verifyNoInteractions¶
The verifyNoInteractions
method verifies whether the mocks, which are provided as arguments, were not used in the test, i.e. if any method was called on a mock, the verification will fail.
The following line of code shows an example of the use of the verifyNoInteractions
method:
verifyNoInteractions(someMockA, someMockB);
verifyNoMoreInteractions¶
What if we verified some of the modeled behaviors in our test, but we are not sure if we covered all of them? In this case, we can use the verifyNoMoreInteractions
method, which will fail the test if verification is missing. This call takes a simple form. Arguments for this method are mock varargs, e.g .:
verifyNoMoreInteractions(mockA, mockB, mockC);
An example which uses all the mechanisms discussed so far can be found [here] (mockito_examples.md # example-a).
lenient vs verifications¶
For most tests we don't need to call the verify
and verifyNoMoreInteractions
methods. Our modeled behavior is automatically verified by mockito. This is because mocks are created in strict mode by default. What is more, the test will * fail * when we model the behavior of mocks that will not be used. This behavior can be changed (and perform all verifications manually) by setting the lenient
attribute on the@ Mock
annotation.
NOTE: Using the
lenient
mode may make sense if we want to minimize duplicate code by use of the test lifecycle method@BeforeEach
. It mocks some behavior for most but not all tests in a given class.
Advanced aspects¶
So far we have modeled a single behavior of a particular method. What if such a method is executed several times in a piece of code? What if we want the method to return different results during the first and second calls? It could be handled this way based on our prior knowledge:
when(someMock.methodA(1)).thenReturn(7);
when(someMock.methodA(1)).thenReturn(10);
and the example above would be incorrect. In order to improve it, we can use the appropriate overload of the thenReturn
method, where we list successive returned results using varargs, e.g.:
when(someMock.methodA(1)).thenReturn(7, 10);
An alternative may be to use the thenAnswer
method, which allows you to programmatically model the behavior and return values. For this purpose, we need to implement a functional interface, e.g .:
final int[] index = { 0 };
when(someMock.someMethod(1L)).thenAnswer(invocation -> {
if (index[0] == 0) {
index[0]++;
return false;
} else if (index[0] == 1) {
return new Random().nextBoolean();
}
return true;
});
times and never¶
When we expect a certain method on a given mock to be called more than once, we cannot use the unary version of verify
for this purpose. For example, if we expect the execute
method on thesomeService
mock to be called twice in our test, we can use the following verification:
verify(someService).execute();
The test will fail with an error:
org.mockito.exceptions.verification.TooManyActualInvocations:
someService.execute();
Wanted 1 time:
....
But was 2 times:
In order to perform the verification, we should pass the second argument to the verify
method together with the expected number of calls. It can be a method call:
never()
if we don't expect any callstimes(int executions)
if we expect a certain number of calls.
Such a call may take the form:
verify(someService, times(2)).execute(); // times is a static method in Mockito class
inOrder¶
Sometimes, in addition to performing a verification, we also want to make sure about the order of method calls on a given mock. A mechanism that can help us is the inOrder
method, which returns the InOrder
object for a given mock. This object allows you to perform verifications in the same way as by calling the verify
and verifyNoMoreInteraction
methods directly, e.g .:
final InOrder inOrder = inOrder(someMock);
inOrder.verify(someMock).getAll();
inOrder.verify(someMock, times(3)).addElement(element);
All of these mechanisms, i.e.:
thenAnswer
times
inOrder
.
were presented inn this example.
void method¶
The mechanisms presented so far allowed us only to model the behavior of methods that returned a certain object (or primitive). The use of when().thenReturn/thenAnswer
makes no sense for type methods that return nothing. To mock such methods, the following can help us:
doNothing()
, which does not do any logic when calling the method on mockdoAnswer()
, which does some logic when calling a method on a mock.
After calling one of the above methods, then we call the when
method pointing to the mock and mocked method, e.g .:
doNothing().when(databaseConnection).open();
You can find a sample use of this method here.
NOTE: Verifying the
void
methods is no different from verifying the methods that return the objects.NOTE: There is also the
doReturn
method which is an alternative to the preferredwhen(...).thenReturn
mechanism. The difference is that the return type is not validated.
Arguments for methods¶
Most often when we mock the behavior of methods and then carry out verifications, we act on the specific values of the arguments. Sometimes we work with complex objects that are difficult to construct for tests and mocks. In this case, we can simplify the test with:
- methods from the
ArgumentMatchers
class - use of the
Captor
class.
ArgumentMatchers¶
In order to mock a method to any input argument, we can use the ArgumentMatchers
class and theany
static method, e.g .:
when(userRepository.findById(any())).thenReturn(Optional.empty());
Similarly, we can verify for any argument:
verify(userRepository).findById(any());
The any
method is one of many in theArgumentMatchers
class. We also have more specialized ones at our disposal, for example:
anyMap
anyList
anyString
.
What if we are working with a multi-argument method and we are not interested in the value of only one argument? Obviously the implementation of this behavior will fail :
when(userRepository.findByIdAndName(any(), "Carl")).thenReturn(Optional.empty());
When mocking the behavior of a multi-argument method and deciding to a method from the ArgumentMatchers
class, we need to use appropriate matchers for all arguments. If you want to use any
as one argument and specific values of other arguments, we need theeq
method, e.g .:
when(userRepository.findByIdAndName(any(), eq("Carl"))).thenReturn(Optional.empty());
You will find examples of tests using ArgumentMatchers
here.
Capturing arguments¶
In case we want to check our assumptions about the argument (which is usually complicated) that is used in calling a certain mocked method, we can use the Captor
object. The ArgumentCaptor
is an object that allows you to capture argument values for further verification. Like mock, ArgumentCaptor
can be created programmatically and declaratively, i.e.:
- with the
ArgumentCaptor.forClass
method - with the
@Captor
annotation.
For example, in order to create an ArgumentCaptor
that allows us to validate aMessage
object, we can use:
ArgumentCaptor<Message> messageCaptor = ArgumentCaptor.forClass(ArgumentCaptor.class); // programmable version
@Captor
private ArgumentCaptor<Message> messageCaptorWithAnnotation; // declarative version
Of course, to use the @Captor
annotation, our test must have aMockitoExtension
extension.
With the ArgumentCaptor
object initialized, we can:
- catch the argument value using the
capture
method during verification - get the captured value using the
getValue
method (or in the case of multiple values -getValues
), which allows us to perform further verifications.
You can find a full example using these possibilities here.
Mockito and BDD¶
If the naming of methods in mockito is unintuitive for us and we prefer the Behavior-Driven Development approach, instead of calls:
when
,then
verify
,
we can use methods from the class BDDMockito
:
given
along with thewill
method group, replacing thewhen
and thethen
method groupthen
andshould
, which replaceverify
.
An example showing the use of these methods can be found here.