You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

执行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();
    }
}

问题分析

  1. Mock注入错误:测试类中deviceControllerFacade使用@MockBean却手动实例化,且未将mock的deviceServiceMock注入到控制器实例中,导致控制器内的deviceService为null,调用getEntity(id)返回null。
  2. 空指针触发点:deviceTO.mergeEntityToTransferObject(device)中device为null,若该方法未做null校验,直接访问device的属性就会抛出空指针。
  3. 返回类型差异原因:当返回类型为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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 21:01:05