mark
mark

Reputation: 417

Getting 400 response code instead of 200 code when testing multipart files upload

I am testing an endpoint to upload an array of multipart files along with a String parameter. I am getting the 400 response instead of the 200 (OK). Any thoughts on why I am getting 400 response, which indicates something wrong with my request object. But when I checked the request, the content type is correct.

uploadMyFiles.java endpoint

@PostMapping(path = "/uploadMyFiles", consumes = { MediaType.MULTIPART_FORM_DATA_VALUE })
public Map<String, String> uploadMyFiles(MultipartFile[] multipartFiles, String userName) {
//..
return statusMap
}

My test case

@Test
 public void testUploadMyFiles() throws Exception {
        byte[] byteContent = new byte[100];
        String userName ="testUser"
        
        MockMultipartFile filePart1 = new MockMultipartFile("files", "file1.pdf", "multipart/form-data", content);
        MultipartFile[] multipartFiles={filePart1}
        
        Object resultMap;
        when(myService.uploadMyFiles(multipartFiles,userName)).thenReturn(Map.of("file1.pdf", "Success"));

        MvcResult result = this.mockMvc.perform(MockMvcRequestBuilders.multipart("/uploadMyFiles")
                            .content(userName)
                            .content("{userName:testUser}") //tried with this too
                            .param("userName", "testUser")) //tried with this too 
                            .andExpect(status().isOk())
                            .andReturn();

        assertEquals(HttpStatus.OK, ((ResponseEntity)result.getAsyncResult()).getStatusCode());
    }

Debug:

MockHttpServletRequest:
      HTTP Method = POST
      Request URI = /uploadMyFiles
       Parameters = {userName=[testUser]}
          Headers = [Content-Type:"multipart/form-data", Content-Length:"23"]
             Body = <no character encoding set>
    Session Attrs = {}

response

MockHttpServletResponse:
           Status = 400
    Error message = null
          Headers = [Vary:"Origin", "Access-Control-Request-Method", "Access-Control-Request-Headers"]
     Content type = null
             Body = 
    Forwarded URL = null
   Redirected URL = null
          Cookies = []

Upvotes: 2

Views: 1381

Answers (1)

xerx593
xerx593

Reputation: 13289

HTTP 400 (in spring-web) indicates, that a RequestMapping only "partially maps" ...

To test this Controller/RequestMapping:

@Controller
public class MyController {
    private static final Logger LOG = LoggerFactory.getLogger(MyController.class);

    @PostMapping(path = "/uploadMyFiles", consumes = {MediaType.MULTIPART_FORM_DATA_VALUE})
    public @ResponseBody
    Map<String, String> uploadMyFiles(MultipartFile[] multipartFiles, String userName) {
        if (multipartFiles == null) {
            LOG.warn("multipartFiles is null!");
        } else {
            LOG.info("{} file{} uploaded.", multipartFiles.length, multipartFiles.length > 1 ? "s" : "");
            if (LOG.isDebugEnabled()) {
                for (MultipartFile mf : multipartFiles) {
                    LOG.debug("name: {}, size: {}, contentType: {}", mf.getOriginalFilename(), mf.getSize(), mf.getContentType());
                }
            }
        }
        // ...
        return Map.of("foo", "bar");
    }
}

This would be the outline of a MockMvc test(, which hits this controller):

package com.example; // package of spring-boot-app

    
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.web.servlet.MockMvc;

import java.nio.charset.StandardCharsets;

import static org.hamcrest.Matchers.containsString;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

@WebMvcTest
class Q66280300ApplicationTests {
    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testUploadMyFiles() throws Exception {
        byte[] byteContent1 = "text1".getBytes(StandardCharsets.US_ASCII);
        byte[] byteContent2 = "text2".getBytes(StandardCharsets.US_ASCII);
        String userName = "testUser";

        // IMPORTANT: name = "multipartFiles" (name of the request parameter)
        MockMultipartFile filePart1 = new MockMultipartFile("multipartFiles", "file1.txt", "text/plain", byteContent1);
        MockMultipartFile filePart2 = new MockMultipartFile("multipartFiles", "file2.txt", "text/plain", byteContent2);

        this.mockMvc.perform(
          multipart("/uploadMyFiles")
          .file(filePart1)
          .file(filePart2)
          .param("userName", userName)
        )
        .andDo(print())
        .andExpect(status().isOk())
        .andExpect(content().string(containsString("{\"foo\":\"bar\"}")));

    }
}

And to find your files (inside the controller, logging, null...) it is crucial, that the name of MockMultipartFile matches the name of the request parameter. (in our case the "default" name of method parameter: "multipartFiles" )

Code bases on Spring-boot-starter:2.4.3.

Upvotes: 3

Related Questions