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

使用MockMvc测试Multipart文件上传接口时遇空指针异常求助

解决MockMvc测试空指针异常问题

问题场景

编写JUnit测试用MockMvc触发REST请求时,出现空指针异常:

java.lang.NullPointerException: Cannot invoke "org.springframework.test.web.servlet.ResultActions.andExpect(org.springframework.test.web.servlet.ResultMatcher)" because the return value of "org.springframework.test.web.servlet.MockMvc.perform(org.springframework.test.web.servlet.RequestBuilder)" is null

对应的控制器方法:

@RequestMapping(value = "data/upload", method = RequestMethod.POST, consumes = MediaType.MULTIPART_FORM_DATA)
@CrossOrigin
@Loggable (activityname = ActivityLogConstants.CONFIG_DATA_UPLOAD, operationName =ActivityLogConstants.UPLOAD_OPERATIONNAME)
public Response importConfigData(HttpServletRequest request, @RequestParam("file") MultipartFile inFile, @RequestParam("rowData") String rowData) throws ServiceException {
    // 业务逻辑
}

原测试代码:

@ExtendWith(SpringExtension.class)
public class ConfigUploadTest {
@MockBean
private MockMvc mockMvc;


@InjectMocks
private UploadController uploadController;

@Test
public void vnfmService_ConfigUploadShouldReturnStatusOkTest() throws Exception {
    MockMultipartFile file = new MockMultipartFile(
            "file", // The request parameter name
            "test.xml", // The original filename (with .xml extension)
            "application/xml", // The content type for XML
            "<?xml version=\"1.0\" encoding=\"UTF-8\"?><data>This is some XML content</data>".getBytes());
    MockMultipartHttpServletRequestBuilder request = MockMvcRequestBuilders.multipart("/data/upload");
    request.file("file", file.getBytes());
    request.param("rowData","{\"test\":\"test\"}");
    mockMvc.perform(request).andExpect(status().isOk());
}
}

需求:不加载完整应用上下文,仅测试单个控制器。

错误原因

  1. MockMvc初始化错误:用@MockBean标注MockMvc是错误的,@MockBean用于模拟Spring容器中的依赖Bean,但MockMvc是测试工具,需要手动构建而非模拟,导致mockMvc实例为null,调用perform时返回null触发NPE。
  2. Multipart请求构建不当:request.file("file", file.getBytes())的用法有误,应该直接传入MockMultipartFile对象而非字节数组。

修复方案

  1. 移除@MockBean注解的MockMvc字段,改为在测试类中手动初始化MockMvc,使用MockMvcBuilders.standaloneSetup()针对单个控制器构建,无需加载完整上下文。
  2. 调整Multipart请求的构建方式,直接将MockMultipartFile对象添加到请求中。
  3. 确保控制器的依赖(如果有的话)通过@InjectMocks或手动注入完成。

完整修复后的测试代码

@ExtendWith(SpringExtension.class)
public class ConfigUploadTest {
    private MockMvc mockMvc;

    @InjectMocks
    private UploadController uploadController;

    @BeforeEach
    void setUp() {
        // 针对单个控制器构建MockMvc,无需加载完整上下文
        mockMvc = MockMvcBuilders.standaloneSetup(uploadController)
                // 若控制器有@CrossOrigin注解,需添加对应处理器
                .addFilter(new CorsFilter(new UrlBasedCorsConfigurationSource()))
                .build();
    }

    @Test
    public void vnfmService_ConfigUploadShouldReturnStatusOkTest() throws Exception {
        MockMultipartFile file = new MockMultipartFile(
                "file",
                "test.xml",
                "application/xml",
                "<?xml version=\"1.0\" encoding=\"UTF-8\"?><data>This is some XML content</data>".getBytes());

        mockMvc.perform(MockMvcRequestBuilders.multipart("/data/upload")
                        .file(file) // 直接传入MockMultipartFile对象
                        .param("rowData", "{\"test\":\"test\"}"))
                .andExpect(status().isOk());
    }
}

额外说明

  • 如果控制器依赖了其他服务(如Service层),需用@Mock标注这些依赖,再通过@InjectMocks注入到控制器中,避免测试时出现依赖缺失问题。
  • standaloneSetup方式适合快速测试单个控制器,无需启动整个Spring上下文,符合你的需求。

内容的提问来源于stack exchange,提问作者manjosh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 18:35:10