Mockito is an open source mocking library for Java. I have been using Mockito for many years, and I am very grateful to the maintainers of this library for their hard work. Although the practice of mocking is a controversial topic in itself, I have found Mockito to be a very useful tool in my testing toolbox.

When I first started using Mockito, I was very excited about the annotations provided with it: @Mock, @Captor, @Spy and @InjectMocks. I thought they were an elegant and concise way to create and inject mocks into my test classes. However, over time, I have come to realise that the annotations are actually not great, and nowadays I tend to avoid them if I can.

Consider the following code:

public class WidgetController {
    private final WidgetService widgetService;

    public WidgetController(WidgetService widgetService) {
        this.widgetService = widgetService;
    }

    public String getWidget() {
        return widgetService.getWidget();
    }
}

It is a very simple controller that simply delegates the call to a service class. Now let’s write a test for this controller using Mockito annotations:

import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.mockito.Mockito.when;

class WidgetControllerTest {
    @Mock
    WidgetService widgetService;

    @InjectMocks
    WidgetController widgetController;

    @Test
    void testGetWidget() {
        when(widgetService.getWidget()).thenReturn("widget");
        assertEquals("widget", widgetController.getWidget());
    }
}

The test fails:

Cannot invoke "WidgetService.getWidget()" because "this.widgetService" is null

Ok, ok, I know what you are thinking: I’m cheating in order to make my case against the annotation. As we know, JUnit does not support the Mockito annotations out of the box. We need to use the MockitoExtension to enable them. It is well documented, but still it is very easy to forget about that. Note that the compiler does not help us here, and instead we get a runtime error. Anyway, the fix is very simple. Just add the @ExtendWith(MockitoExtension.class). Now the test passes! We are done here, right?

Not quite. Contrary to dependency injection in Spring or Guice, @InjectMocks does a “best effort” attempt to inject the dependencies, and may fail silently. For example, let’s say the next version of the WidgetController class adds a new dependency on FeatureFlagService:

public class WidgetController {
    private final WidgetService widgetService;
    private final FeatureFlagService featureFlagService;

    public WidgetController(WidgetService widgetService,
                            FeatureFlagService featureFlagService) {
        this.widgetService = widgetService;
        this.featureFlagService = featureFlagService;
    }

    public String getWidget() {
        return widgetService.getWidget();
    }
}

After refactoring WidgetController to add the new dependency on FeatureFlagService, the unit test still passes. How is that possible? There is no mock of FeatureFlagService, in the test, right? The answer is that when Mockito cannot find a suitable @Mock to inject, it silently injects a null value. In this case, not only the compiler does not help us, but the runtime does not help us either.

To be fair, once the WidgetController starts making use of the FeatureFlagService, the test will fail with a NullPointerException, assuming that the unit test has 100% coverage. But the point is that the test should have failed immediately after the refactoring. If the application uses Spring or Guice to inject the dependencies of WidgetController, the developer will probably assume that its fields can not be null. But in reality, they can be null if the @InjectMocks annotation is used. That is one of the reasons I recommend writing code to enforce the invariant, by rewriting the constructor to use Objects.requireNonNull:

    public WidgetController(WidgetService widgetService,
                            FeatureFlagService featureFlagService) {
        this.widgetService = requireNonNull(widgetService);
        this.featureFlagService = requireNonNull(featureFlagService);
    }

That way, the test will fail immediately after the refactoring, regardless of whether the unit test has 100% coverage or not:

org.mockito.exceptions.misusing.InjectMocksException: 
Cannot instantiate @InjectMocks field named 'widgetController' of type 'class WidgetController3'.
You haven't provided the instance at field declaration so I tried to construct the instance.
However the constructor or the initialization block threw an exception : null

That clear failure serves a reminder to update the test and add:

    @Mock
    FeatureFlagService featureFlagService;

Still, this is not perfect. If later on the FeatureFlagService stops being useful and it is removed from WidgetController, the test will still pass. Other than explicit verifications of the number of calls to the mocks, which are often neglected, nothing will remind us to remove the mock from the test and its associated programming.

Another problem with @InjectMocks is that it is limited regarding what it can inject. Let’s imagine that we change again the constructor of WidgetController to accept a String parameter:

    public WidgetController(WidgetService widgetService, String baseUrl) {
        this.widgetService = requireNonNull(widgetService);
        this.baseUrl = requireNonNull(baseUrl);
    }

Once again, the compiler does not complain. Thanks to the requireNonNull statements, at least the test fails with a NullPointerException, otherwise it would have passed despite the test not having been updated with the new parameter. How to fix it? Mockito cannot mock String, therefore we cannot add a @Mock String baseUrl to the test, and anyway, it would not make sense to mock a String. The @InjectMock annotation can only inject fields declared with @Mock or @Spy, so it cannot inject the baseUrl parameter using it. We have to revert to the old way of creating the object under test:

WidgetController widgetController = new WidgetController(widgetService, "http://example.com/");

The question is: where should we put that line? We have several options:

  • In the @BeforeEach method: this is the most common approach, but it is verbose. It requires a new method to be added to the test class.
  • At the beginning of the @Test method. However, if we have multiple test methods, we have to repeat the line in each one.
  • In the field declaration: this is the most concise, but when combined with the @Mock annotation, it leads to errors like this:
@ExtendWith(MockitoExtension.class)
class WidgetControllerTest {
    @Mock
    WidgetService widgetService;

    WidgetController widgetController = new WidgetController(widgetService, "baseUrl");

    @Test
    void testGetWidget() {
        when(widgetService.getWidget()).thenReturn("widget");
        assertEquals("widget", widgetController.getWidget());
    }
}

At first glace, the code looks correct, but the test fails with a NullPointerException. The problem is that the widgetService field is not initialized when the widgetController field is initialised. The Mockito extension does not initialise the fields annotated with @Mock until the test object is fully created.

Note that if we had not used the requireNonNull statement in the constructor, the NullPointerException would have been thrown by the widgetService.getWidget() call, making it harder to understand the cause of the problem because it would look like a regular test failure rather than a failure to initialise the test object.

In conclusion, I recommend avoiding the Mockito annotations and instead creating the mocks and the object under test explicitly. This does not add much boilerplate to the test, and it makes the test easier to understand and more robust to refactorings. It also avoids having to abandon the annotations at a later stage, when the object under test becomes more complex. Finally, it also makes it possible to declare the mocks and the object under test as final, entirely eliminating the class of mutability problems that can arise when using Mockito annotations.

The resulting test looks as follows, using the convenient mock() method with type inference. Note that the removal of the annotations results in fewer lines of code and fewer characters, dispelling the myth that annotations are more concise.

class WidgetControllerWithoutAnnotationsTest {
    final WidgetService widgetService = mock();

    final WidgetController widgetController = new WidgetController(widgetService, "baseUrl");

    @Test
    void testGetWidget() {
        when(widgetService.getWidget()).thenReturn("widget");
        assertEquals("widget", widgetController.getWidget());
    }
}

Finally, let me insist in the importance of adding requireNonNull statements to the constructors. This is a good practice for any constructor that accepts an object reference and saves it to a field without using it immediately. As we have seen, it helps to detect problems early, leading to better error messages and more robust programs.