使用 TestRestTemplate 进行 Multipart POST 请求的集成测试返回 400

Posted

技术标签:

【中文标题】使用 TestRestTemplate 进行 Multipart POST 请求的集成测试返回 400【英文标题】:Integration test with TestRestTemplate for Multipart POST request returns 400 【发布时间】:2017-09-09 16:04:18 【问题描述】:

我知道类似的问题已经出现过几次了,但是按照建议的修复方法并没有解决我的问题。

我有一个带有以下端点的简单控制器:

@RequestMapping(method = RequestMethod.POST)
public ResponseEntity<String> singleFileUpload(@RequestParam("file") MultipartFile file) 
    log.debug("Upload controller - POST: ", file.getOriginalFilename());

    // do something

我正在尝试使用 Spring TestRestTemplate 为其编写集成测试,但我所有的尝试都以 400 - Bad Request 结尾(没有日志说明控制台中出了什么问题)。

控制器内部的日志没有被命中,所以在到达那里之前就失败了。

您能否看看我的测试并指出我做错了什么?

@Test
public void testUpload() 
    // simulate multipartfile upload
    ClassLoader classLoader = getClass().getClassLoader();
    File file = new File(classLoader.getResource("image.jpg").getFile());

    MultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
    parameters.add("file", file);

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);

    HttpEntity<MultiValueMap<String, Object>> entity = new HttpEntity<MultiValueMap<String, Object>>(parameters, headers);

    ResponseEntity<String> response = testRestTemplate.exchange(UPLOAD, HttpMethod.POST, entity, String.class, "");

    // Expect Ok
    assertThat(response.getStatusCode(), is(HttpStatus.OK));

【问题讨论】:

看起来您在测试中设置了 ContentType = MULTIPART_FORM_DATA。但是您的控制器未设置为接收此类请求。尝试使用 consumes = MediaType.MULTIPART_FORM_DATA_VALUE 请发布测试类的其余部分,因为有一些我无法在此处重现的缺失部分 @BrunoLeite 谢谢你的建议。我试过了,但事实并非如此。 你真的用你的响应实体返回一个字符串吗?您是否验证了 400 响应正文中的内容,以防它提供更多详细信息?您是否考虑过使用调试器运行它或实现异常处理程序? 是的,正在返回字符串。它在集成测试之外工作正常。在这个测试中,我发布了。我什至没有得到调试线,所以在它到达控制器主体之前发生了一些事情。 【参考方案1】:

我尝试了以下方法:

@Test
public void testUpload() 
    LinkedMultiValueMap<String, Object> parameters = new LinkedMultiValueMap<String, Object>();
    parameters.add("file", new org.springframework.core.io.ClassPathResource("image.jpg"));

    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.MULTIPART_FORM_DATA);

    HttpEntity<LinkedMultiValueMap<String, Object>> entity = new HttpEntity<LinkedMultiValueMap<String, Object>>(parameters, headers);

    ResponseEntity<String> response = testRestTemplate.exchange(UPLOAD, HttpMethod.POST, entity, String.class, "");

    // Expect Ok
    assertThat(response.getStatusCode(), is(HttpStatus.OK));

如您所见,我使用 org.springframework.core.io.ClassPathResource 作为文件的对象,并且效果非常好

希望有用

安杰洛

【讨论】:

首先我不清楚是什么导致了这个问题。多部分控制器是否与java.io.File 不兼容?无论如何,它确实像一个魅力,谢谢。这为我节省了很多工作! @Smajl 我猜想其余模板能够更好地管理org.springframework.core.it.Resource 实现而不是传递一个简单的java.io.File 我知道这已经很老了,但我能问你,你的“image.jpg”文件在你的项目中的什么位置? @bot_bot 在我的代码中,图像位于类路径下(如果您使用的是 maven,则在 src/main/resources 中)【参考方案2】:

如果你想使用java.nio.file.Path,也可以使用FileSystemResource

包裹:org.springframework.core.io.FileSystemResource

例如,您可以这样做:

new FileSystemResource(Path.of("src", "test", "resources", "image.jpg"))

完整代码示例:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class UploadFilesTest 

    private final TestRestTemplate template;

    @Autowired
    public UploadFilesTest(TestRestTemplate template) 
        this.template = template;
    

    @Test
    public void uploadFileTest() 
        var multipart = new LinkedMultiValueMap<>();
        multipart.add("file", file());

        final ResponseEntity<String> post = template.postForEntity("/upload", new HttpEntity<>(multipart, headers()), String.class);

        assertEquals(HttpStatus.OK, post.getStatusCode());
    

    private HttpHeaders headers() 
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.MULTIPART_FORM_DATA);
        return headers;
    

    private FileSystemResource file() 
        return new FileSystemResource(Path.of("src", "test", "resources", "image.jpg"));
    

休息控制器:

@RestController
public class UploadEndpoint 
    @PostMapping("/upload")
    public void uploadFile(@RequestParam("file") MultipartFile file) 
        System.out.println(file.getSize());
    

【讨论】:

以上是关于使用 TestRestTemplate 进行 Multipart POST 请求的集成测试返回 400的主要内容,如果未能解决你的问题,请参考以下文章

spring boot使用TestRestTemplate集成测试 RESTful 接口

Spring TestRestTemplate 未正确自动装配

spring boot 测试无法注入 TestRestTemplate 和 MockMvc

假设检验总结以及如何用python进行假设检验(scipy)

MU puzzle

R中具有多个mu值的多列的单样本T检验