Avoid annotations with Mockito

On the Mockito mailing list and on Stackoverflow, there are a significant number of questions that actually reveal that the person asking did not perfectly understand what the Mockito annotations do. Indeed, the annotations are rather magical and their behavior is definitely not obvious.

Since they are mostly syntactic sugar (although whether they actually improve readability is debatable), my recommendation is to do without them entirely. Here are a few examples.

 

The most common issue is when using @Mock:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
	@Mock
	private MyDependency dependency;
	private ClassUnderTest instance = new ClassUnderTest(dependency);
}

Here, the issue is that @Mock is read by Brice Dutheil’s┬áMockitoJUnitRunner only after the test class is instantiated, that is, after the ClassUnderTest is instantiated. So ClassUnderTest only received a null value, not the mocked dependency.

A fix/variant uses the @InjectMock annotation:

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {
	@Mock
	private MyDependency dependency;
	@InjectMocks
	private ClassUnderTest instance = new ClassUnderTest();
}

This one is more tricky. Although it will work in simple cases, it tends to behave strangely in more complex classes, as pointed out by in a recent thread on the Mockito mailing list: “since 1.9.5, mocks created by Mockito.mock() will be included in the list of injection candidate; that does explain that the mock of ClassToInjected is injected in itself somewhere in an upper class.”
We should also note that it discourages making dependencies final, which is not a good thing.

My preference would be to write something like that:

public class MyClassTest {
	private MyDependency dependency = mock(MyDependency.class);

	private ClassUnderTest instance = new ClassUnderTest(dependency);
}

Yes, there are some redundancies (in the names of the dependency class), but I believe this much is acceptable in a test.

Other available annotations include @Spy and @Captor, which behave in ways similar to @Mock. However, since spies and captors should be used sparingly, to say the least, there are few cases when it makes sense to declare them in the attributes section. Intentions are a lot clearer when spies and argument captors are instantiated within methods anyway.

In summary, the benefits provided by the annotations provided by Mockito do not seem to justify the reduction in readability. I recommend avoiding them.

About Eric Lefevre-Ardant

Independent technical consultant.
This entry was posted in java. Bookmark the permalink.

8 Responses to Avoid annotations with Mockito

  1. Hi Eric

    You may appreciate to use http://docs.mockito.googlecode.com/hg/org/mockito/MockitoAnnotations.html#initMocks%28java.lang.Object%29 in your setUp method in order to control the injection, or create a junit rule to do that.
    Does this would do the right thing in the desired order or i missed something ?

  2. Certainly, but I am not happy with how the resulting code reads. Having a setUp() method just for calling initMocks() does not seem very desirable to me.

    Every time I can, I avoid creating a setUp() method, as it makes harder to follow the flow of the test.

  3. So here is the rule:

    package my.package;

    import org.junit.rules.TestWatchman;
    import org.junit.runners.model.FrameworkMethod;
    import org.mockito.MockitoAnnotations;

    public class MockitoJUnitRule extends TestWatchman { // depends on JUnit Version
    private final Object testObject;

    public MockitoJUnitRule(final Object testObject) {
    super();
    this.testObject = testObject;
    }

    @Override
    public void starting(final FrameworkMethod method) {
    MockitoAnnotations.initMocks(testObject);
    }
    }

  4. Well, a Rule is used via an annotation, right? That rather defeats the purpose of my post ;-)

  5. Sarthak Nigam says:

    Hey Eric,
    You won’t be able to do that if your ClassUnderTest does not have a constructor that accepts the mock or a setter to set the mock, or am I missing something?

  6. Hm… yes, I think this is true. It seems that Mockito will try to inject the property directly if no constructor nor setter is present (see http://docs.mockito.googlecode.com/hg/latest/org/mockito/InjectMocks.html). Do note that, in the case of ambiguity in the types of the dependencies, the injection is happening in a non-obvious fashion.

    For example, this code throws a NullPointerException:

    @RunWith(MockitoJUnitRunner.class)
    public class TestMockito {
    @Mock
    private MyDependency secondDependency;
    @InjectMocks
    private ClassUnderTest instance = new ClassUnderTest();

    @Test
    public void test() {
    instance.doSomething();
    }

    public static class ClassUnderTest {
    MyDependency dependency;
    MyDependency secondDependency;

    public void doSomething() {
    secondDependency.println();
    }
    }

    public static class MyDependency {
    public void println() {
    System.out.println(“Hello world”);
    }
    }
    }

    I’d argue that the very fact that this is not blindingly obvious is an argument in favour of avoiding annotations… and make dependencies obvious by changing the code under test to allow for injection in a normal, manual way. This approach is also very much in the spirit of Mockito: its goal is not really to work with existing code, but rather to guide the creation of new code. Other tools are better at working with existing code bases.

  7. Sarthak Nigam says:

    Yeah they should definitely make it clearer, I recently started working on test cases and was confused for a while for how it worked. Thanks for the info

  8. Rich says:

    Ok, so it’s 6 years later, but I wanted to add a thought. I try to avoid instantiating member variables when they are declared as a member of the test class. In other words, I try to avoid this:

    private ClassUnderTest instance = new ClassUnderTest();

    The reason? It doesn’t give you a nice clean new object for each test. So your tests are not stateless / completely independent of each other. In the vast majority of cases, this is fine and not a problem – but the potential is there. Instantiating them in a @BeforeEach setup method ensures you get a shiny new object for each test with no residual state from another method. It would also resolve the problem that you mentioned of having the mock instantiated after new ClassUnderTest() is called. I don’t need to be afraid of @Mock.

    I would argue that @Mock makes your intentions very clear and is much more readable. I’m not sure how this is debatable. It may not reduce your code line count, but @Mock & @InjectMocks make it very clear which variables are mocked dependencies and which is the CUT. That said, there are times when Mockito.mock() is better, and I think we should just use good judgment when those times are.

Leave a Reply

Your email address will not be published.