Null after @InjectMocks
Asked Answered
S

7

23

I am having some troubles passing a dependency while unit testing with JUnit.

Consider these pieces of code:

This is the dependacy injecton into the class which i want to test, lets call it Controller.

@Inject private FastPowering fastPowering;  

And this is the unit test:

@RunWith(MockitoJUnitRunner.class)
public class ControllerTest {

@Mock
FastPowering fastPower;

@InjectMocks
Controller controller;

@Test
public void test() {
    assertEquals(
            (controller.computeAnswer(new BigDecimal(2), 2)).longValue(),
            (long) Math.pow(2, 2));
    }
}

It seems that fastPower is null, please explain how to fix that. Null pointer exception , because of calling the @injected field (fastPower) inside the .computeAnswer method)

Edit:

Solved i should have read about the difference between @Mock and @Spy...

Due to a lot of comments I am adding some more context to the solution

The difference is that in mock, you are creating a complete mock or fake object while in spy, there is the real object and you just spying or stubbing specific methods of it. While in spy objects, of course, since it is a real method, when you are not stubbing the method, then it will call the real method behavior.

If fastPower is annotated as @Mock it's methods are dummy, yet controller.computeAnswer depends on them to compute. One must provide behaviour.

If spy is used without stubbing then the real implementation of fastPower is being executed which eventually returns desired value.

Another option is to use a real FastPowering instance

https://github.com/mockito/mockito/wiki/Using-Spies-(and-Fakes) https://github.com/mockito/mockito/wiki/Mocking-Object-Creation

And some stackoverflow thread outlining the difference Mocking vs. Spying in mocking frameworks

Short Answer: Replace @Mock with @Spy and should be working fine

Strenta answered 21/11, 2014 at 15:48 Comment(5)
Is the method static or final?Barker
it is neather final nor staticStrenta
"Solved i should have read about the difference between @Mock and @Spy..." would it be possible to post and accept your solution as an answer? That may help other users with the same problem and close this thread.Gynoecium
Please describe the solution rather than a silly clue.Linkage
@Strenta kindly add the solution you found as an answer.Tripod
D
33

Use MockitoAnnotations.initMocks to initiate the @Mock and @InjectMocks objects. Your test would look something like:

@Mock
FastPowering fastPower;

@InjectMocks
Controller controller;

@Before
public void setup() {
    MockitoAnnotations.initMocks(this);
}

@Test
public void test() {
    ....
}
Dylan answered 21/11, 2014 at 19:41 Comment(5)
The same issue, NPE after @InjectMocks and MockitoAnotations.initMocks(this)Zohar
@Zohar were you able to resole this issue? if yes, how?Repercussion
Solution does not workAnamariaanamnesis
I was able to get it working with BeforeEach instead of BeforeAnamariaanamnesis
init mock looks to be deprecatedDomineer
A
14

I was using the wrong @Test annotations, If you want to use @InjectMocks and @Mock in your Mockito Test, then you should

  1. add @ExtendWith(MockitoExtension.class) annotation on your test class
  2. annotate your test methods with @Test (org.junit.jupiter.api.Test) and not the @Test (org.junit.Test) annotation. be careful with the import that you are using for this annotation.

This works on mockito-core:3.6.28

Accident answered 14/9, 2022 at 10:55 Comment(1)
Thank you! Why they would keep these incompatible annotations so closely named is beyond me.Disfigurement
W
4

After debugging I found a reason. This is because of the org.powermock.core.MockRepository#instanceMocks collection. It doesn't contain a mock for a field with @InjectMocks annotation (Controller controller in your case). To solve it try to use the @Spy annotation in the field declaration with initializing of them and @PrepareForTest above the class declaration:

@PrepareForTest(Controller.class)
@RunWith(MockitoJUnitRunner.class)
public class ControllerTest {
    
    @Mock
    FastPowering fastPower;
    
    @Spy
    @InjectMocks    
    Controller controller = new Controller();
    
    @Test
    public void test() {
        //...
    }
}

In my case it helped. Using of the Mockitoannotations.initMocks(this) method is not required, it doesn't affect the result.

Whitley answered 28/10, 2018 at 22:23 Comment(0)
U
3

I fixed this by removing the extraneous new instance I was creating in my @Before method (see example below). It was also fixed by moving MockitoAnnotations.initMocks(this) after initializing myClass, but since Mockito created myClass anyway, that solution was inferior.

// Note - you may need @mock(name="foo") to help mockito differentiate props
//        if they have the same type
@Mock
private Thing something;

@InjectMocks
private MyClass myClass;

@Before
 public void setUp() {
    MockitoAnnotations.initMocks(this); // Moving this below the next line fixed it...
    myClass = new MyClass() // But just remove this line and let Mockito do the work
}
Urdar answered 11/11, 2019 at 17:27 Comment(1)
Errr, in such case you don't really Inject your MyCLass, but rather directly create an instance of it. So, @InjectMocks becomes completely useless with it.Procrustean
T
2

Useful to note that the use of MockitoAnnotations.initMocks(this); needs to come at the end of your @Before/setUp() method.

If it is at the top of your setUp() then it may cause other mocked classes to not be initialised.

I ran into this error myself just now and placing .initMocks at the end of my @Before solved my issue.

Thermoluminescence answered 23/10, 2019 at 9:51 Comment(0)
E
0

the NPE could be also thrown inside the tested method (probably because some of the injected bean's method is not defined by when().thenReturn() ) to check this run the test with:

-DtrimStackTrace=false

Eversole answered 13/6 at 8:43 Comment(0)
V
-1

2 more things to check:

  1. Mocking the behaviours of fastPower. What should this mocked object return, when it methods are called? i.e. when(fastPower.doSomething()).thenReturn(some_kind_of_object);
  2. Check if the controller.computeAnswer() does not return NULL for the input of new BigDecimal(2), 2)).longValue(), (long) Math.pow(2, 2).
Vaudeville answered 15/12, 2015 at 13:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.