Spring MVC测试中Service初始化失败,Mock后仍报空指针求助
问题原因分析
你遇到的空指针问题根源在于测试中控制器实例的创建和依赖注入逻辑混乱:
- 你通过
MockMvcBuilders.standaloneSetup(new BillEntryController())手动创建了控制器实例,但这个手动new出来的对象里的billEntryService没有被注入,完全是null状态。 - 你手动mock的
billEntryService只是赋值给了测试类的成员变量,并没有关联到这个手动创建的控制器实例上,所以控制器调用服务时必然空指针。 @WebMvcTest注解已经会自动配置Spring MVC测试环境,包括生成MockMvc实例和管理控制器Bean,不需要手动构建MockMvc。
修复方案
修改测试代码,利用Spring Boot测试的注解自动管理mock对象和控制器依赖:
@AutoConfigureMockMvc @WebMvcTest(BillEntryController.class) // 精准指定要测试的控制器,缩小上下文范围 class BillEntryControllerTest { @MockBean // 用该注解自动创建Mock服务,并注入到Spring管理的控制器实例中 private BillEntryService billEntryService; @Autowired private MockMvc mockMvc; // 移除手动创建MockMvc的setup方法 @Test public void checkUpdateBill() throws Exception { // 定义mock服务的行为:如果控制器调用addOrUpdateBill,什么都不做 doNothing().when(billEntryService).addOrUpdateBill(any(BillEntry.class)); // 额外:如果测试用例传入了billId,需要mock getBillEntryById的返回值,避免空指针 when(billEntryService.getBillEntryById(anyInt())).thenReturn(new BillEntry()); this.mockMvc .perform(MockMvcRequestBuilders.post("/bill-entry/saveBillEntry").accept(MediaType.TEXT_HTML) .param("amount", "10.0") .param("owner", "User") .param("property", "Prop") .param("receiptNumber", "ABC") .param("accountName", "AC") .param("billerName", "BN") .param("datePaid", "20/10/2022") .param("dateDue", "20/10/2022") .param("billId", "1") // 测试更新场景需传入billId,触发getBillEntryById逻辑 .param("redirect", "/bill-entry")) .andExpect(model().errorCount(0)) .andExpect(status().is3xxRedirection()); // 控制器返回的是重定向,预期3xx状态码而非200 } }
关键注意点
@MockBean的作用:Spring Boot会自动将这个mock服务注入到上下文里的控制器实例中,替代真实的服务实现,确保控制器能调用到mock对象。- 状态码修正:控制器最后返回
redirect:前缀的字符串,属于HTTP重定向,测试时应该断言is3xxRedirection()而非isOk(),否则测试会失败。 - 额外mock逻辑:如果测试用例触发了控制器中
getBillEntryById的分支(比如传入了billId),必须mock该方法的返回值,否则会因为返回null导致后续空指针。 - 简化注解:移除了
@Transactional和不必要的@ContextConfiguration(如果TestAppContext是测试专用配置,可保留),@WebMvcTest已经足够支撑控制器层测试。
内容的提问来源于stack exchange,提问作者BoomRamada
相关产品推荐
相关产品推荐

