Syed
Syed

Reputation: 2597

InjectMocks object is null in Unit testing

This is my first junit tests using Mockito. I'm facing the issue of NPE for the service that was used in @InjectMocks. I looked at the other solutions, but even after following them, it shows same. Here is my code.

@RunWith(MockitoJUnitRunner.class)

public class CustomerStatementServiceTests {

    @InjectMocks
    private BBServiceImpl bbService;

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

    }

/**
 *  This test is to verify SUCCESS response
 */

@Test
public void testSuccess() { 

    BBResponse response = bbService.processDetails(txs);
    assertEquals("SUCCESSFUL" ,response.getResult());
}
}

BBServiceImpl

@Service
public class BBServiceImpl implements BBService {

final static Logger log = Logger.getLogger(BBServiceImpl.class);



public BBResponse process(List<Customer> customers) { 
  // My business logic goes here
}
}

Pom.xml

        <!-- https://mvnrepository.com/artifact/org.mockito/mockito-core -->
    <dependency>
        <groupId>org.mockito</groupId>
        <artifactId>mockito-core</artifactId>
        <version>2.23.4</version>
        <scope>test</scope>
    </dependency>




    <!-- https://mvnrepository.com/artifact/junit/junit -->
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.12</version>
        <scope>test</scope>
    </dependency>

My "bbService" object is null here

Am I missing anything here?

Upvotes: 4

Views: 11858

Answers (2)

second
second

Reputation: 4259

After a discussion which provided additional informations, the answer can be summarized as a maven configuration problem between junit4 and junit5.

java.lang.NullPointerException
at com.cts.rabo.CustomerStatementServiceTests.testSuccess(CustomerStatementServiceTests.java:83)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:675)
at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) 
...

The stacktrace showed a clear usage of the junit5 engine.

The pom also included the following dependency:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>2.2.5.RELEASE</version>
    <scope>test</scope>
    <exclusions>
        <exclusion>
            <groupId>org.junit.vintage</groupId>
            <artifactId>junit-vintage-engine</artifactId>
        </exclusion>
    </exclusions>
</dependency>

Before Spring Boot 2.2.0.RELEASE, spring-boot-starter-test included junit4 dependency transitively. Spring Boot 2.2.0 onwards, Junit Jupiter is included instead.

And according to this answer the exclusion seems to prevent the execution.

Removing the exlusion solved the problem for me.


I recommend a switch to junit5 if there are no apparent requirements to use junit4.

Check this answer for more infos on how to use mockito together with junit5.

Upvotes: 3

Mark Bramnik
Mark Bramnik

Reputation: 42431

I think you misuse the @InjectMocks annotation

It makes sense if you have a real class (usually the one you want to test) that has dependencies, which are initialized with constructor injection or setter injection.

BBServiceImpl on the other hand has no real dependencies.

Now, you could create mocks for those dependencies manually and then create an instance of the class under test or let mockito identify the way to create the class and "inject" the mocks for you. You'll still want to create mocks by means of using @Mock annotation because you do want to specify expectations during the test.

Here is an example of how it should look like:

public interface BBService {
    int process(String s); // simplified but still illustrates the concept
}

public class BBServiceImpl implements BBService {
    //here is a dependency that should be mocked when we will test BBServiceImpl class

    private  SizeCalculator sizeCalculator;

    // I use constructor injection here
    public BBServiceImpl(SizeCalculator sizeCalculator) {
        this.sizeCalculator = sizeCalculator;
    }

    public int process(String s) {
        // here is a dependency invocation
        return sizeCalculator.calculateSize(s);
    }
}

As you see, the implementation has a real dependency that can be mocked during the test.

Now, this dependency looks like:

public interface SizeCalculator {
    int calculateSize(String s);
}

public class SizeCalculatorImpl implements SizeCalculator {
    public int calculateSize(String s) {
        return s.length();
    }
}

Ok, now lets create a test for the BBServiceImpl. We don't want to create a SizeCalculatorImpl and prefer to supply a mock for it and specify an expectation on the runtime generated proxy created out of the interface SizeCalcluator:

@RunWith(MockitoJUnitRunner.class)
public class MockitoInjectMocksTest {

    // I need a reference on my mock because I'll specify expectations during the tests
    @Mock
    private SizeCalculator sizeCalculator;

    @InjectMocks
    private BBServiceImpl underTest;

    @Test
    public void testInjectMocks() {
        Mockito.when(sizeCalculator.calculateSize("hello")).thenReturn(5);

       int actual = underTest.process("hello");

        assertEquals(actual, 5);
    }
}

Note the line

@InjectMocks
private BBServiceImpl underTest;

Mockito will find the constructor and inject the mock

Of course in this case we could do it manually:

@RunWith(MockitoJunitRunner.class)
public class SampleManualTest {
    @Mock
    private SizeCalculator sizeCalc;

    private BBServiceImpl underTest;
    @Before
    public void init() {
        this.underTest = new BBServiceImpl(sizeCalc);
    }
}

But think about the case where there are many dependencies in class BBServiceImpl, InjectMocks can be more convenient in this case + if the setter injection is used, this way can save explicit calls to the setters.

Upvotes: 2

Related Questions