Reputation: 417
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
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