Testing MultipartFile with WebtestClient - java

I am writing unit tests for my controller class. I am using spring webflux. Hence I am writing tests with WebTestClient. Here is my controller method
#PutMapping("/updatedocument/{documentType}")
public ResponseEntity<String> updateDocument(#PathVariable String documentType,
#RequestParam("file") MultipartFile file) {
...................
}
This code is working when I call from Postman or any rest client. I am having difficulty in writing unit test. I am getting
"Required MultipartFile parameter 'file' is not present"
Error . Here is my test method.
#Test
void updateDocument() throws IOException {
.............
MultipartBodyBuilder multipartBodyBuilder = new MultipartBodyBuilder();
multipartBodyBuilder.part("file", new ClassPathResource("somefile"))
.contentType(MediaType.MULTIPART_FORM_DATA)
webTestClient.put()
.uri("/customer/updatedocument/ID")
.body(BodyInserters.fromMultipartData(multipartBodyBuilder.build()))
.exchange()
.expectStatus().isOk();
}
Any suggestion is much appreciated. Please NOTE. I am using WebTestClient and not MovkMvc

I was able to solve this issue. The main culprit is my controller method and not the test method.
Had to change couple of things in controller method. When using spring web flux(reactive) we should use
1.#RequestPart instead of #RequestParam
2. FilePart instead of MultipartFile
So the controller method will look like this.
#PutMapping("/updatedocument/{documentType}")
public ResponseEntity<String> updateDocument(#PathVariable DocumentType documentType,
#RequestPart("file") FilePart filePart) {
.....................
}
You can convert FilePart to File object.

Related

Spring Boot: file upload testing

I'm trying to create a Unit test in a Spring REST application. the test is related to an endpoint for MultipartFile uploading.
Here is the arguments that my method accepts within my #RestController
#PostMapping("/upload")
public ResponseEntity uploadFile(#HasFileName #RequestParam("file") MultipartFile file,
#NotEmpty #RequestParam("entity") String entity, #NotEmpty #RequestParam("language") String language,
#NotEmpty #RequestParam("lastModified") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime lastModifiedDateTime,
#NotEmpty #RequestParam("createdDate") #DateTimeFormat(iso = DateTimeFormat.ISO.DATE_TIME) LocalDateTime createdDateTime)
throws IOException {
I can't find a way to get this information from the multipart inside the unit test. I can't find documentation on making spring unit test for Spring REST, so any inputs would be appreciated.
#Test
public void testUploadFile() throws Exception{
ResultMatcher ok = MockMvcResultMatchers.status().isOk();
String filename = "test.txt";
File file = new File("/test" + filename);
file.delete();
MockMultipartFile mockMultipartFile = new MockMultipartFile("file", "test.txt", "multipart/form-data", "test data".getBytes());
upload.uploadFile(mockMultipartFile, mockMultipartFile.getName()) //missing more data
//more logic on how to assert that the file is not empty
}
In principle the test should validate the outcome of the uploadFile method():
ResultActions result =
this.mockMvc.perform(multipart("/svc").file(file)
.header(HttpHeaders.AUTHORIZATION,
"Bearer token"))
.andExpect(content().string("response"))
.andExpect(status().isOk());
The file is an input parameter so I dont understand why you want to verify it is not empty: it could be useful I believe to verify the interactions with other mocks (for instance checking the file is used in a FileService.save() method)

Spring Test Controller upload Json and Multipart File Always return 405

I want to test my Spring Controller that has input json and multipart file, however even if the function works, I can't get the test works
I tried using MockMvcRequestBuilders.fileUpload but the test always get 405
The controller:
#PutMapping(value = "/upload", consumes = {"multipart/form-data"})
public BaseResponse<SomeModel> updateSomeModelContent(
#ApiIgnore #Valid #ModelAttribute MandatoryRequest mandatoryRequest,
#PathVariable("id") String id,
#RequestParam("file") MultipartFile file,
#RequestParam("json") String json) throws IOException {
final CommonSomeModelRequest request = JSONHelper.convertJsonInStringToObject(json, CommonSomeModelRequest.class);
return makeResponse(someModelSer.updateContent(id, request, mandatoryRequest, file));
}
The test:
#Test
public void updateCountryContentSuccessTest() throws Exception {
MockMultipartFile file1 = new MockMultipartFile("file", "filename-1.jpeg", "image/jpeg", "some-image".getBytes());
MockMultipartFile file2 = new MockMultipartFile("json", "", "application/json","{\"exampleAttr\": \"someValue\"}".getBytes());
when(this.someModelService
.updateContent(id, request, MANDATORY_REQUEST, file1))
.thenReturn(someModelUpdatedContent);
MockHttpServletRequestBuilder builder = MockMvcRequestBuilders
.fileUpload("/upload/{id}", id)
.file(file1)
.file(file2)
.requestAttr("mandatory", MANDATORY_REQUEST);
this.mockMvc.perform(builder)
.andExpect(status().isOk());
verify(this.someModelService)
.updateContent(id, request, MANDATORY_REQUEST, file1);
}
The result status is always 405, I don't know how to make it 200
MockMvcRequestBuilders.multipart(...) create MockMultipartHttpServletRequestBuilder which support only POST.
But in your controller you use POST. You should change PUT to POST mapping in the rest controller.
Also according to RFCs we should use POST instead of PUT on multipart upload.
Have a look at Spring MVC Framework: MultipartResolver with PUT method

How To Pass Multipart Image File as parameter to a POST REST Service

I have been trying to upload a multipart file in my Java Spring MVC Web Application using a POST REST service method. I have using the following REST service method to upload the file and this works fine when i choose the file using Postman REST service.
#RequestMapping(value="/upload", method=RequestMethod.POST)
public #ResponseBody String handleFileUpload( #RequestParam("file") MultipartFile file, ModelMap model)
{
//codes
}
But when i tried to pass the multipart file as parameter to a POST REST service method in the controller. Its not working fine. So how can i pass multipart file as a queryparam to a POST REST service method.
In my controller class I have:
#RequestMapping(value = "/upload-image", method = RequestMethod.POST)
public String uploadProfileImage(#RequestParam("fileUpload") MultipartFile fileUpload, Model model, HttpServletRequest request, HttpServletResponse response)
{
// codes
}
I have the following bean in my root-context.xml file
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
</bean>
Any help is appreciated.
It is easy and a little bit strange. Do use #PathVariable instead of #RequestParam. I faced with this situation couple month ago. I do not know why it so, but snippet below works in my project.
#ResponseBody
#RequestMapping(method = RequestMethod.POST, value = "/upload-image", consumes = MULTIPART_FORM_DATA_VALUE, produces = APPLICATION_JSON_VALUE)
public String uploadProfileImage(#PathVariable("fileUpload") MultipartFile file) {
// ...
}
Look at JerseyRestClientMultipartUpload.java to get example how to send MultiPart using Jersey.
final MultiPart multiPart = new FormDataMultiPart()
.field("description", "Picture of Jabba the Hutt", MediaType.TEXT_PLAIN_TYPE)
.field("characterProfile", jsonToSend, MediaType.APPLICATION_JSON_TYPE)
.field("filename", fileToUpload.getName(), MediaType.TEXT_PLAIN_TYPE)
.bodyPart(fileDataBodyPart);
multiPart.setMediaType(MediaType.MULTIPART_FORM_DATA_TYPE);
// POST request final
final WebResource resource = client.resource(API_URI)
ClientResponse response = resource.type("multipart/form-data").post(ClientResponse.class, multiPart);

Test file upload Spring MVC

I would like to test file uploading by REST API. I found it quite confusing to send file as RequestParam instead of RequestBody.
Controller method:
#PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public
#ResponseBody
ResponseEntity<String> uploadFile(
#RequestParam(name = "file") MultipartFile multipartFile,
#RequestParam(name = "path") String path) {
logger.debug("File upload REST requested");
return new ResponseEntity<>(fileService.uploadFile(
multipartFile, path),
HttpStatus.OK);
}
1.Now I would like to test it and I've used mocks. While debugging it, I see that mock service working but method exactly with this arguments is not invoked, so the test if failed. How could I handle this?
#Test
public void testUploadFile() throws Exception {
String mockName = "mock";
MockMultipartFile mockMultipartFile = new MockMultipartFile(mockName, mockName.getBytes());
when(mockFileService.uploadFile(mockMultipartFile, rootDir)).thenReturn("success");
mockMvc.perform(MockMvcRequestBuilders.fileUpload("/files/upload")
.file("file", mockMultipartFile.getBytes())
.param("path", rootDir))
.andExpect(status().isOk())
.andExpect(content().string("success"));
verify(mockFileService, times(1)).updateFile(mockMultipartFile, rootDir);
verifyNoMoreInteractions(mockFileService);
}
2.How could I test this with Postman? I see that I can send file in Body, but how could I send it as param?
EDIT:
I've changed the method, but the problem is not there:
Argument(s) are different! Wanted:
mockFileService.uploadFile(
org.springframework.mock.web.MockMultipartFile#61bd0845,
"/"
);
Looks like method are using 2 instances of MockMultipartFile. And the second question is still open, how could this method be tested from Postman?
Yes, the test case fails, because
the problem is in your test case at the end, you are verifying the call to updateFile() which is incorrect as in your controller you are using uploadFile(), you MUST use the same method to verify.
So, you need to change the verify line as below:
verify(mockFileService, times(1)). uploadFile(mockMultipartFile, rootDir);
In other words, Mockito verify validates the number of times a method is invoked as you are trying to verify the call on a different method (not being used in controller), it is failing.

spring MockMvc testing for model attribute

I have a controller method for which i have to write a junit test
#RequestMapping(value = "/new", method = RequestMethod.GET)
public ModelAndView getNewView(Model model) {
EmployeeForm form = new EmployeeForm()
Client client = (Client) model.asMap().get("currentClient");
form.setClientId(client.getId());
model.addAttribute("employeeForm", form);
return new ModelAndView(CREATE_VIEW, model.asMap());
}
Junit test using spring mockMVC
#Test
public void getNewView() throws Exception {
this.mockMvc.perform(get("/new")).andExpect(status().isOk()).andExpect(model().attributeExists("employeeForm")
.andExpect(view().name("/new"));
}
I am getting NullPointerException as model.asMap().get("currentClient"); is returning null when the test is run, how do i set that value using spring mockmvc framework
As an easy work around you should use MockHttpServletRequestBuilder.flashAttr() in your test:
#Test
public void getNewView() throws Exception {
Client client = new Client(); // or use a mock
this.mockMvc.perform(get("/new").flashAttr("currentClient", client))
.andExpect(status().isOk())
.andExpect(model().attributeExists("employeeForm"))
.andExpect(view().name("/new"));
}
The response is given as string chain (I guess json format, as it is the usual rest service response), and thus you can access the response string via the resulting response in this way:
ResultActions result = mockMvc.perform(get("/new"));
MvcResult mvcResult = result.andExpect(status().isOk()).andReturn();
String jsonResponse = mvcResult.getResponse().getContentAsString();
And then you can access to the response via getResponse().getContentAsString(). If json/xml, parse it as an object again and check the results. The following code simply ensures the json contains string chain "employeeForm" (using asertJ - which I recommend)
assertThat(mvcResult.getResponse().getContentAsString()).contains("employeeForm")
Hope it helps...

Categories

Resources