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

如何测试接收POJO与MultipartFile数组的POST接口?

MockMVC测试Multipart接口遇到400错误的解决方法

我是MockMVC新手,已经成功编写部分基础测试,但在测试一个需携带POJO和MultipartFile数组两个参数的POST接口时遇到瓶颈——请求无法到达服务层,始终返回400 Bad Request错误。

我的测试代码

@Test
public void vytvorPodnetTest() throws Exception {
    var somePojo = new SomePojo();
    somePojo.setSomeVariable("test_value");

    var roles = List.of("TEST_USER");
    var uid = "00000000-0000-0000-0000-000000000001";

    MockMultipartFile[] attachments = {new MockMultipartFile("file1.txt", "file1.txt", "text/plain", "file1 content".getBytes()),
            new MockMultipartFile("file2.txt", "file2.txt", "text/plain", "file2 content".getBytes())};

    MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart("/some-pojo/create");
    builder.with(req -> {
        req.setMethod("POST");
        return req;
    });

    MvcResult result = mockMvc.perform(builder.file(attachments[0]).file(attachments[1])
                    .param("SomePojo", new ObjectMapper().writeValueAsString(somePojo))
                    .file(attachment[0])
                    .with(TestUtils.generateJWTToken(uid, roles)))
            .andExpect(status.isOk())
            .andReturn();
}

控制器方法

@PostMapping(value = "/create", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
public UUID createPojo(
            @RequestPart(value = "SomePojo") SomePojo somePojo,
            @RequestPart(value = "attachments", required = false) MultipartFile[] attachments) {

    return pojoService.create(somePojo, attachments);
}

问题分析与修正方案

核心问题点

  • POJO参数传递方式错误:控制器用@RequestPart接收POJO,测试中却用.param()传递,这是普通表单字段的传递方式,无法被@RequestPart解析。
  • 文件参数命名不匹配:控制器中文件数组的参数名是attachments,但测试创建MockMultipartFile时未指定该参数名,Spring无法将文件绑定到数组。
  • 重复添加文件:测试代码中重复调用了.file(attachments[0]),导致请求中存在重复的文件part。
  • 多余的请求方法设置:MockMvcRequestBuilders.multipart()默认就是POST请求,无需手动设置。

修正后的测试代码

@Test
public void vytvorPodnetTest() throws Exception {
    var somePojo = new SomePojo();
    somePojo.setSomeVariable("test_value");
    ObjectMapper objectMapper = new ObjectMapper();
    String pojoJson = objectMapper.writeValueAsString(somePojo);

    var roles = List.of("TEST_USER");
    var uid = "00000000-0000-0000-0000-000000000001";

    // 第一个参数是请求参数名,需和控制器的@RequestPart("attachments")匹配
    MockMultipartFile[] attachments = {
            new MockMultipartFile("attachments", "file1.txt", "text/plain", "file1 content".getBytes()),
            new MockMultipartFile("attachments", "file2.txt", "text/plain", "file2 content".getBytes())
    };

    MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.multipart("/some-pojo/create");
    // 用.part()传递POJO,指定媒体类型为application/json
    builder.part(new MockPart("SomePojo", pojoJson.getBytes(StandardCharsets.UTF_8)))
            .file(attachments[0])
            .file(attachments[1])
            .with(TestUtils.generateJWTToken(uid, roles));

    MvcResult result = mockMvc.perform(builder)
            .andExpect(status.isOk())
            .andReturn();
}

也可以用.requestPart()方法简化POJO传递:

// 替换builder.part(...)这一行
builder.requestPart("SomePojo", objectMapper.writeValueAsBytes(somePojo))
        .contentType(MediaType.APPLICATION_JSON)

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.04 02:35:22