
本文详解在使用 @injectmocks 和 @spy/@mock 时,因手动 new 实例覆盖依赖注入而导致的 nullpointerexception,并提供正确配置 mockito 的完整实践方案。
本文详解在使用 @injectmocks 和 @spy/@mock 时,因手动 new 实例覆盖依赖注入而导致的 nullpointerexception,并提供正确配置 mockito 的完整实践方案。
在基于 Spring Boot 的单元测试中,常见误区是:既声明了 @InjectMocks 字段,又在测试方法内手动创建服务实例——这会导致 Spring 或 Mockito 注入的模拟对象被忽略,所有 @Autowired 的依赖(如 runSummaryRepository)保持为 null,最终在业务逻辑中调用其方法时触发 NullPointerException。
你提供的测试代码中,问题正源于这一关键错误:
@Test
public void TestprocessCustomerRunSummaries() {
final RunSummaryServiceImpl runSummaryServiceImpl = new RunSummaryServiceImpl(); // ❌ 错误:手动 new 实例
// ...
runSummaryServiceImpl.processCustomerRunSummaries(runs); // 此时 runSummaryRepository == null
}
该行完全绕过了 @InjectMocks 机制,新建的对象未注入任何 mock 依赖,因此 processCustomerRunSummary() 中调用 runSummaryRepository.findByCustomerIdAndSolutionNumberAndDownloadDatetime(...) 时抛出 NPE(堆栈显示第 74 行崩溃,正是此方法内部)。
✅ 正确做法是:直接使用已由 Mockito 自动注入依赖的字段实例,无需手动构造:
@SpringBootTest
public class RunSummaryServiceImplTest {
@Mock // 推荐使用 @Mock 而非 @Spy,除非需部分真实行为
private RunSummaryRepository runSummaryRepository;
@Mock
private FileRepository fileRepository;
@InjectMocks
private RunSummaryServiceImpl runSummaryServiceImpl; // ← 这个字段已被注入 mock 依赖
@BeforeEach // 替换已废弃的 @Before(JUnit 5+)
void setUp() {
MockitoAnnotations.openMocks(this); // 替换已废弃的 initMocks()
}
@Test
void testProcessCustomerRunSummaries() {
// 准备测试数据
Collection<runsummary> runs = List.of(
new RunSummary("customer1", 1, Timestamp.valueOf("2023-05-30 10:00:00")),
new RunSummary("customer2", 2, Timestamp.valueOf("2023-05-30 11:00:00")),
new RunSummary("customer3", 3, Timestamp.valueOf("2023-05-30 12:00:00"))
);
// ✅ 直接调用已注入依赖的实例
runSummaryServiceImpl.processCustomerRunSummaries(runs);
// 验证行为(例如 repository 是否被调用)
verify(runSummaryRepository, times(3))
.findByCustomerIdAndSolutionNumberAndDownloadDatetime(anyString(), anyInt(), any(Timestamp.class));
}
}</runsummary>
? 关键修正点总结:
- 移除 new RunSummaryServiceImpl():这是根本原因,必须删除;
- 统一使用 @Mock:@Spy 会创建真实对象并代理部分方法,但 RunSummaryRepository 等接口通常无实现类,@Mock 更安全、语义更清晰;
- 升级 Mockito 初始化方式:MockitoAnnotations.initMocks(this) 已废弃,改用 MockitoAnnotations.openMocks(this)(JUnit 4/5 均适用);若使用 JUnit 5,推荐搭配 @ExtendWith(MockitoExtension.class),可省略手动初始化;
- 命名与规范:测试方法名建议使用 camelCase(如 testProcessCustomerRunSummaries),避免 Testxxx 前缀(JUnit 不依赖命名约定);
- 补充验证逻辑:虽 processCustomerRunSummaries 返回 void,仍可通过 verify() 断言 mock 对象是否按预期被调用,确保逻辑路径正确。
? 进阶建议:若需测试数据库交互逻辑(如 createCustomerRunSummary),应考虑使用 @DataJpaTest + H2 内存数据库进行集成测试;而纯单元测试应聚焦于服务层逻辑隔离,依赖全部 mock 化。
遵循以上实践,即可彻底规避因注入失效引发的空指针异常,构建稳定、可维护的 Mockito 单元测试。










