Mockito: Why You Should Not Use InjectMocks Annotation to Autowire Fields

People like the way how Mockito is able to mock Spring’s auto-wired fields with the @InjectMocks annotation. When I read this post of Lubos Krnac last week, I thought I should explain why I think the use of InjectMocks is a bad signal and how you should avoid it. Hint: it’s about visibility.

Let’s say we have a PlannerServiceImpl which delegates to a PlannerClient. Uses Spring for auto-wiring all together; there’s no constructor, but Spring is able to use field injection.

@Service
public class PlannerServiceImpl implements PlannerService {
    private static final Logger LOG = LoggerFactory.getLogger(PlannerServiceImpl.class);

    @Autowired
    private PlannerClient plannerClient;

    @Override
    public Long createWeddingPlan() {
        try {
            CreateWeddingPlanResponse response = plannerClient.createWeddingPlan();
            return convert(response).getId();
        } catch (Exception e) {
            LOG.error("Unable to create wedding plan", e);
            return null;
        }
    }

An associated test could look like:

@RunWith(MockitoJUnitRunner.class)
public class PlannerServiceImplTest {

    @Mock
    private PlannerClient plannerClient;

    @InjectMocks
    private final PlannerServiceImpl plannerService = new PlannerServiceImpl();

    @Test
    public void testCreateWeddingPlanWhenClientReturnsUndefinedResponseThenNullIsReturned() throws Exception {
        when(plannerClient.createWeddingPlan()).thenReturn(null);

        final Long actual = plannerService.createWeddingPlan();

        assertThat(actual, is(nullValue()));
    }

The org.mockito.InjectMocks annotation can be seen as an equivalent of Spring’s own dependency injection. The Javadoc states:

Mockito will try to inject mocks only either by constructor injection, setter injection, or property injection in order and as described below. If any of the following strategy fail, then Mockito won’t report failure; i.e. you will have to provide dependencies yourself.

(Whoever would design this to fail silently at all?)

So what if someone decides to create a new dependency, say an AuditService and upgrades a bunch of services by adding it as an additional property, also marked as @Autowired?

@Service
public class PlannerServiceImpl implements PlannerService {
    private static final Logger LOG = LoggerFactory.getLogger(PlannerServiceImpl.class);

    @Autowired
    private PlannerClient plannerClient;

    @Autowired
    private AuditService auditService;

    @Override
    public Long createWeddingPlan() {
        try {
            CreateWeddingPlanResponse response = plannerClient.createWeddingPlan();
            auditService.addEntry("Wedding plan created.");
            return convert(response).getId();
        }

The test will fail, probably on a NullPointerException on a missing AuditService – and it is not visible why. InjectMocks will fail silently and there’s no indication the test needs this. Did I already ask whoever would design something like this to fail silently?

If you’re doing TDD or not (and we are able to change the test first) – clients of this code don’t know about an additional dependency, because it’s completely hidden. You shouldn’t use InjectMocks to deal with injecting private fields (err..or at all) , because this kind of Dependency Injection is evil – and signals you should change your design.

There, I said it.

Fix #1: Solve your design and make your dependencies visible.

Create a constructor. Pass along the PlannerClient.

@Service
public class PlannerServiceImpl implements PlannerService {
    private static final Logger LOG = LoggerFactory.getLogger(PlannerServiceImpl.class);

    private final PlannerClient plannerClient;

    @Autowired
    public PlannerServiceImpl(final PlannerClient plannerClient) {
        this.plannerClient = plannerClient;
    }

Now, when there are more dependencies needed, they’re clearly in sight because the constructor says so. So don’t go creating a bunch of setters now – they still don’t force you to pass along your required dependencies!

@Service
public class PlannerServiceImpl implements PlannerService {
    private static final Logger LOG = LoggerFactory.getLogger(PlannerServiceImpl.class);

    private final PlannerClient plannerClient;

    private final AuditService auditService;

    @Autowired
    PlannerServiceImpl(PlannerClient plannerClient, AuditService auditService) {
        this.plannerClient = plannerClient;
        this.auditService = auditService;
    }

The test itself won’t compile any more (luckily, because of the way we’ve been instantiating the field as plannerService = new PlannerServiceImpl()!) as soon as e.g. the AuditService is added to the constructor. So it’s time to..

Fix #2: Get rid of @InjectMocks

There’s no need to use @InjectMocks anymore. Instead instantiate the class-under-test properly in a @Before-annotated method – where it belongs, passing along all needed dependencies.

@RunWith(MockitoJUnitRunner.class)
public class PlannerServiceImplTest {

@Mock
private PlannerClient plannerClient;

@Mock
private AuditService auditService;

private PlannerServiceImpl plannerService;

@Before
void setUp() {
    plannerService = new PlannerServiceImpl(plannerClient, auditService);
}

Luckily, Lubos – which I mentioned earlier – completely independently came to the same conclusion in the mean time 🙂

Mixing JUnit, Hamcrest and Mockito: Explaining java.lang.NoSuchMethodError: org/hamcrest/Matcher.describeMismatch

Let’s say you’ve introduced JUnit and Hamcrest matchers to your project by adding the following to your pom.xml

<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.11</version>
</dependency>
<dependency>
	<groupId>org.hamcrest</groupId>
	<artifactId>hamcrest-all</artifactId>
	<version>1.3</version>
</dependency>

For the first time there’s a possibility when running a successful test it’s all peaches and fine, but when it fails you’ll be confronted with a NoSuchMethodError.

java.lang.NoSuchMethodError: org/hamcrest/Matcher.describeMismatch(Ljava/lang/Object;Lorg/hamcrest/Description;)V
at org.hamcrest.collection.IsIterableContainingInOrder$MatchSeries.describeMismatch(IsIterableContainingInOrder.java:83)
at org.hamcrest.collection.IsIterableContainingInOrder$MatchSeries.isMatched(IsIterableContainingInOrder.java:66)
at org.hamcrest.collection.IsIterableContainingInOrder$MatchSeries.matches(IsIterableContainingInOrder.java:52)
at org.hamcrest.collection.IsIterableContainingInOrder.matchesSafely(IsIterableContainingInOrder.java:25)
at org.hamcrest.collection.IsIterableContainingInOrder.matchesSafely(IsIterableContainingInOrder.java:14)
at org.hamcrest.TypeSafeDiagnosingMatcher.matches(TypeSafeDiagnosingMatcher.java:55)
at org.hamcrest.MatcherAssert.assertThat(MatcherAssert.java:12)
at org.junit.Assert.assertThat(Assert.java:865)

This could be due to the fact that JUnit itself brings along its own version of Hamcrest as a transitive dependency. Now if you would be using JUnit 4.11 it would depending on Hamcrest 1.3 already – see here. Getting above error would be weird – since the describeMismatch method is present in org.hamcrest.Matcher interface.

There seems to be an older version of org.hamcrest.Matcher present on the classpath to which org.junit.Assert.assertThat delegates.

If you run this from Eclipse or IntelliJ, there’s a high chance that the IDE uses its own version of JUnit instead of your Maven dependency. You should first rule that out by running mvn clean install -Dtest<yourtest> outside your IDE .

If that still doesn’t work, check which dependencies are present on the classpath by running mvn dependency:tree -Dscope=test which might give you some results like this:

[INFO] +- org.mockito:mockito-all:jar:1.9.5:test
[INFO] +- com.tngtech.java:junit-dataprovider:jar:1.5.0:test
[INFO] |  \- com.google.code.findbugs:annotations:jar:2.0.1:test
[INFO] \- org.hamcrest:hamcrest-all:jar:1.3:test
[INFO] ------------------------------------------------------------------------

This lists all dependencies in the test scope – which possibly allows you to see whether or not another Matcher class is brought in. If you have a lot of dependencies, what often might help is to look for the specifc class within the IDE.

Finding classes in Eclipse
Ctrl-Shift-T to find classes in Eclipse

If we we’re looking in Eclipse for the Matcher classes we e.g. could see that there’s also one in mockito-all-1.9.5.jar – see example screenshot above. Bugger! Seems mockito-all is incompatible with JUnit 4.11 for backwards-compatibility reasons. The Hamcrest version 1.1 Matcher has been packaged within the dependency, so we can not exclude it.

Luckily for us, Mockito allows to us to use a mockito-core dependency instead of mockito-all. Running a dependency check (with dependency:tree or online) shows us it depends on hamcrest-core.

[INFO] +- org.mockito:mockito-core:jar:1.9.5:test
[INFO] |  \- org.hamcrest:hamcrest-core:jar:1.1:test

We can exclude it in the pom.xml and – no more NoSuchMethodError. Here’s the final combination of dependencies in our case:

<dependency>
	<groupId>org.mockito</groupId>
	<artifactId>mockito-core</artifactId>
	<version>1.9.5</version>
	<exclusions>
		<exclusion>
			<artifactId>hamcrest-core</artifactId>
			<groupId>org.hamcrest</groupId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>junit</groupId>
	<artifactId>junit</artifactId>
	<version>4.11</version>
	<exclusions>
		<exclusion>
			<artifactId>hamcrest-core</artifactId>
			<groupId>org.hamcrest</groupId>
		</exclusion>
	</exclusions>
</dependency>
<dependency>
	<groupId>org.hamcrest</groupId>
	<artifactId>hamcrest-all</artifactId>
	<version>1.3</version>
</dependency>

Featured image on top of this post courtesy of the The Junit / Green Lantern Oath.