执行RestAPI DeleteById的JUnit测试时遇NestedServletException问题求助
问题解决:DeleteById接口返回DeviceTO时JUnit测试空指针异常
问题现象
执行RestAPI的DeleteById方法JUnit测试时抛出异常:
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
将控制器方法返回类型改为Device时测试正常,但业务要求必须返回DeviceTO(DTO),需解决该空指针问题。
代码片段
测试类
@RunWith(MockitoJUnitRunner.class) @EnableWebMvc public class DeviceControllerFacadeTest { @Mock DeviceServiceImpl deviceServiceMock; @MockBean DeviceControllerFacadeImpl deviceControllerFacade = new DeviceControllerFacadeImpl(); MockMvc mockMvc; @Before public void setUp() { MockitoAnnotations.initMocks(this); mockMvc = MockMvcBuilders.standaloneSetup(deviceControllerFacade) .build(); } @After public void tearDown() throws Exception { } @Test public void DeleteDeviceById() throws Exception { mockMvc.perform(MockMvcRequestBuilders .delete("/device/delete?id=336").accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)) .andDo(MockMvcResultHandlers.print()); } }
控制器接口
@DeleteMapping("/delete") DeviceTO deleteDevice(@RequestParam("id") long id);
控制器实现类
@Override public DeviceTO deleteDevice(long id) { try { begin(); Device device = deviceService.getEntity(id); //deviceService.delete(device); DeviceTO deviceTO = new DeviceTO(); deviceTO.mergeEntityToTransferObject(device); commit(); return deviceTO; } catch (final Exception exception) { LOG.error(exception.getMessage(), exception); throw exception; } finally { cleanupTransaction(); } }
问题分析
- Mock注入错误:测试类中
deviceControllerFacade使用@MockBean却手动实例化,且未将mock的deviceServiceMock注入到控制器实例中,导致控制器内的deviceService为null,调用getEntity(id)返回null。 - 空指针触发点:
deviceTO.mergeEntityToTransferObject(device)中device为null,若该方法未做null校验,直接访问device的属性就会抛出空指针。 - 返回类型差异原因:当返回类型为
Device时,可能测试逻辑中mock了deviceService.getEntity的返回值,或者代码路径未触发空指针(比如直接返回null不会报错,但返回DTO需要转换)。
解决方案
1. 修复测试类的Mock注入
改用@InjectMocks自动注入依赖,确保控制器能拿到mock的deviceService:
@RunWith(MockitoJUnitRunner.class) @EnableWebMvc public class DeviceControllerFacadeTest { @Mock DeviceServiceImpl deviceServiceMock; @InjectMocks // 替换@MockBean,自动注入mock的deviceServiceMock到控制器 DeviceControllerFacadeImpl deviceControllerFacade; MockMvc mockMvc; @Before public void setUp() { MockitoAnnotations.initMocks(this); mockMvc = MockMvcBuilders.standaloneSetup(deviceControllerFacade) .build(); } @Test public void DeleteDeviceById() throws Exception { // Mock deviceService.getEntity的返回值,避免null Device mockDevice = new Device(); mockDevice.setId(336); // 可设置其他必要属性 Mockito.when(deviceServiceMock.getEntity(336)).thenReturn(mockDevice); mockMvc.perform(MockMvcRequestBuilders .delete("/device/delete?id=336") .accept(MediaType.APPLICATION_JSON) .contentType(MediaType.APPLICATION_JSON)) .andDo(MockMvcResultHandlers.print()) .andExpect(MockMvcResultMatchers.status().isOk()); // 添加状态断言 } }
2. 优化控制器实现类的空指针防护
在转换DTO前先校验device是否为null,避免空指针:
@Override public DeviceTO deleteDevice(long id) { try { begin(); Device device = deviceService.getEntity(id); if (device == null) { throw new RuntimeException("Device not found with id: " + id); // 或返回自定义响应 } //deviceService.delete(device); DeviceTO deviceTO = new DeviceTO(); deviceTO.mergeEntityToTransferObject(device); commit(); return deviceTO; } catch (final Exception exception) { LOG.error(exception.getMessage(), exception); throw exception; } finally { cleanupTransaction(); } }
3. 完善DTO转换方法的健壮性
确保mergeEntityToTransferObject方法能处理null参数:
public class DeviceTO { public void mergeEntityToTransferObject(Device device) { if (device == null) { return; // 或设置默认值,避免空指针 } // 正常转换逻辑 this.setId(device.getId()); // 其他属性转换 } }
验证结果
修改后重新运行测试,控制器将正常返回DeviceTO,测试不会再抛出空指针异常,同时能正确验证接口响应。
内容的提问来源于stack exchange,提问作者MR Int
相关产品推荐
相关产品推荐

