在单元测试中,InjectMocks对象为空

时间:2020-03-01 18:17:32

标签: java junit mockito

这是我第一次使用Mockito进行junit测试。我正面临@InjectMocks中使用的服务的NPE问题。我查看了其他解决方案,但即使遵循这些解决方案,它也显示相同的结果。这是我的代码。

@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>

我的“ bbService”对象在此处为空

我在这里错过了什么吗?

2 个答案:

答案 0 :(得分:2)

在提供了更多信息的讨论之后,答案可以概括为junit4junit5之间的Maven配置问题。

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) 
...

stacktrace显示了junit5引擎的清晰用法。

pom还包括以下依赖项:

<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>

在Spring Boot 2.2.0.RELEASE之前,spring-boot-starter-test可传递地包含junit4依赖项。从Spring Boot 2.2.0开始,将包含Junit Jupiter。

根据此answer,排除似乎阻止了执行。

删除排他权为我解决了这个问题。


如果没有明显的使用junit5的要求,我建议切换到junit4

选中此answer,以获取有关如何将mockitojunit5一起使用的更多信息。

答案 1 :(得分:1)

我认为您滥用@InjectMocks批注

如果您有一个真正的类(通常是要测试的类)具有依赖项,则可以使用构造函数注入或setter注入对其进行初始化。 另一方面,

BBServiceImpl没有真正的依赖性。

现在,您可以手动为那些依赖项创建模拟,然后创建要测试的类的实例,或者让模拟来确定创建类的方式并为您“注入”模拟。您仍然希望通过使用@Mock注释来创建模拟,因为您确实想在测试期间指定期望值。

这是一个看起来像的例子:

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);
    }
}

如您所见,该实现具有真实的依赖关系,可以在测试期间进行模拟。

现在,这种依赖关系看起来像:

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

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

好吧,现在让我们为BBServiceImpl创建一个测试。我们不想创建一个SizeCalculatorImpl,而是希望为其提供一个模拟并为通过接口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);
    }
}

注意该行

@InjectMocks
private BBServiceImpl underTest;

Mockito将找到构造函数并注入模拟

在这种情况下,我们当然可以手动进行:

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

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

但是考虑一下在类BBServiceImpl中存在许多依赖项的情况,在这种情况下,InjectMocks可以更加方便+如果使用了setter注入,这种方式可以保存对setter的显式调用。

相关问题