Skip to content

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 valuefalsewill 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" forbooleanor an empty list for the Listobject. We can override this behavior by using theanswerattribute 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 calls
  • times(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 mock
  • doAnswer(), 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 preferred when(...).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 the will method group, replacing the when and thethen method group
  • then and should, which replace verify.

An example showing the use of these methods can be found here.