这是我第一次使用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());
}
}
@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
}
}
<!-- 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”对象在此处为空
我在这里错过了什么吗?
答案 0 :(得分:2)
在提供了更多信息的讨论之后,答案可以概括为junit4
和junit5
之间的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,以获取有关如何将mockito
与junit5
一起使用的更多信息。
答案 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的显式调用。