11

I have gone through some of the blogs in order to understand the basics of how Mockito annotations work.

However I am facing a doubt as to when does one go for manually instantiating the field annotated with @InjectMocks i.e

@InjectMocks
A a = new A();

And when does one rely on MockitoAnnotations.initMocks() functionality to do the same :

@InjectMocks
A a;

Does this depend on the JunitTestRunner that we employ to run the test cases or is it dependent on the Mockito framework version?

acdcjunior
  • 132,397
  • 37
  • 331
  • 304
lakshaya agnani
  • 121
  • 1
  • 1
  • 5
  • Hi, I see you're new to SO. If you feel an answer solved the problem, please mark it as 'accepted' by clicking the green check mark. This helps keep the focus on older SO which still don't have answers. Check it out here: http://meta.stackexchange.com/a/5235/219205 – acdcjunior Oct 29 '15 at 17:28

3 Answers3

12

It depends if you are using (declaring) the runner or not.

If you use the runner, you don't need to call MockitoAnnotations.initMocks() yourself - the runner calls it for you.

Usually we go for the runner. When you want to use other runners, though (like Spring's), you can call .initMocks() yourself.

Just to be clear, the MockitoAnnotations.initMocks(this) will:

  • Instantiate the field annotated with @InjectMocks
  • Create a mock version of every field annotated with @Mock
  • Inject the @Mocks in the @InjectMocks variable's fields (or call its constructors or use its setters - it depends on what kind of Dependency Injection you use)

Mockito runner, initMocks and rule code samples

The three code samples below should be equivalent.

With runner:

This first snippet uses the runner, making the call to initMocks() unnecessary.

@RunWith(MockitoJUnitRunner.class)
public class MyClassTest {

    @Mock private MyDependency myDependency;
    @InjectMocks private MyClass myClass;

    @Test
    public void myClass_should_get_stuff_from_dependency() {
        when(myDependency.getStuff()).thenReturn("stuff!");

        assertThat(myClass.getDependencyStuff(), is("stuff!"));
    }
}

Without runner + with manual call to .initMocks():

This other does not use the runner, thus the need for the setUp() method calling our initMocks() friend.

// notice there is no runner
public class MyClassTest {

    @Mock private MyDependency myDependency;
    @InjectMocks private MyClass myClass;

    // but now you have to call initMocks() yourself
    @Before
    public void setUp() throws Exception {
          MockitoAnnotations.initMocks(this);
    }

    @Test
    public void myClass_should_get_stuff_from_dependency() {
        when(myDependency.getStuff()).thenReturn("stuff!");

        assertThat(myClass.getDependencyStuff(), is("stuff!"));
    }
}

Without runner or manual call, using @Rule:

Finally, as pointed out in the comments (thanks @StefanBirkner), since version 1.10.17, there is also the possibility of using a JUnit @Rule called MockitoRule:

public class MyClassTest {

    @Rule
    public MockitoRule rule = MockitoJUnit.rule();

    @Mock private MyDependency myDependency;
    @InjectMocks private MyClass myClass;

    @Test
    public void myClass_should_get_stuff_from_dependency() {
        when(myDependency.getStuff()).thenReturn("stuff!");

        assertThat(myClass.getDependencyStuff(), is("stuff!"));
    }
}
Community
  • 1
  • 1
acdcjunior
  • 132,397
  • 37
  • 331
  • 304
  • It exists a third option: Mockito's JUnit rule: http://site.mockito.org/mockito/docs/current/org/mockito/junit/MockitoRule.html – Stefan Birkner Oct 28 '15 at 23:52
  • @StefanBirkner You are correct! I took the liberty of adding a sample of that `@Rule` to the answer. Thanks for reminding us! – acdcjunior Oct 29 '15 at 00:09
  • Thanks for adding the example. – Stefan Birkner Oct 29 '15 at 09:15
  • great examples...especially the runner vs init example. in light of InjectMocks annotation, if mockito fails to inject by either of constructor, setter or property, mockito doesn't report and expects developer to know! that's almost dangerous...could you clearify more on injectmocks annotation? – Nirmal Aug 21 '18 at 03:12
  • @Nirmal Yeah, mockito ignores it, but hopefully your test fails, right? :) – acdcjunior Mar 22 '19 at 23:12
2

In general, the decision to instantiate an object which is annotated with @InjectMocks or not is a code style choice. In the majority of cases there will be no difference as Mockito is designed to handle both situations.

However, there is some differences which I have outlined below.

@InjectMocks decouples a test from changes to the constructor.

In the same way using a Dependency Injection framework decouples your production code from changes to the constructor. Allowing Mockito to instantiate an instance of the class for you decouples your test code from changes to the constructor. This means any future changes to the class constructor can be done without causing compilation errors in the unit test.

In my opinion this is the biggest difference and the biggest advantage of @InjectMocks.

Mockito will always call the "biggest" constructor

Note: This difference is only relevant when the code you are working with does not follow best practices.

When there is multiple constructors in a class, Mocktio will call the constructor with the most parameters, the "biggest" constructor.

This only has an impact when,

  1. A "small" constructor contains logic.
  2. This logic is required for the class to function correctly.
  3. The "biggest" constructor does not invoke the next "smallest" constructor.

This is considered bad practice because,

  1. Placing logic within a constructor should be avoided whenever possible.
  2. When there is multiple constructors within a class each constructor should first invoke the constructor before it.
fluffy88
  • 79
  • 1
  • 6
1

Thanks for your valuable thoughts. But still it doesn't answer the question as to why one goes for manually instantiating the field annotated with @InjectMocks when the instantiation should be handled by a call to MockitoAnnotations.initMocks().

I get the following error when trying to run the test file :

Caused by: org.mockito.exceptions.base.MockitoException: Field 'student' annotated with @InjectMocks is null. Please make sure the instance is created before MockitoAnnotations.initMocks(); Example of correct usage:

class SomeTest {
    @InjectMocks private Foo foo = new Foo();

    @Before public void setUp() {
        MockitoAnnotations.initMock(this);

I searched further and found out that the error is thrown if one is using an older version of Mockito framework.

http://myshittycode.com/category/testing/mockito/

Paul Samsotha
  • 205,037
  • 37
  • 486
  • 720
lakshaya agnani
  • 121
  • 1
  • 1
  • 5
  • 1
    Yes. To fix that error, just update the mockito version to a newer one. Right now, the preferred version is `1.10.19`, [which is the latest](http://mvnrepository.com/artifact/org.mockito/mockito-core). Update it and everything should work as I pointed in the answer. – acdcjunior Oct 29 '15 at 14:08
  • This answer is still applicable, but initMocks() is now deprecated, and the [current docs](https://www.javadoc.io/doc/org.mockito/mockito-core/3.4.0/org/mockito/MockitoAnnotations.html) indicate to now use openMocks instead – kugo2006 Jan 12 '22 at 23:06