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)
Related
I am working on a Spring Boot Project where I want to send JSON data and Multipart File (Image) in a single API call. For this, I referred- https://blogs.perficient.com/2020/07/27/requestbody-and-multipart-on-spring-boot/#:~:text=Usually%20we%20add%20%40RequestBody%20and,So%2C%20annotation%20should%20be%20altered.
My Controller is-
#PostMapping(value = "/create",consumes = {MediaType.APPLICATION_JSON_VALUE,MediaType.MULTIPART_FORM_DATA_VALUE})
public ResponseEntity<SuccessResponse<PostDto>> createPost(
#RequestPart("post") String post,
#RequestPart("image") MultipartFile file,
#RequestParam(name = "userid") Integer uid,
#RequestParam(name = "categoryid") Integer categoryId) {
log.info("Filename :" + file.getOriginalFilename());
log.info("Size:" + file.getSize());
log.info("Contenttype:" + file.getContentType());
//convert the post string to POJO
PostDto postDto=postService.getJson(post);
//Now create the post
PostDto newPost = this.postService.createPost(postDto, uid, categoryId, file);
SuccessResponse<PostDto> successResponse = new SuccessResponse<>(AppConstants.SUCCESS_CODE,
AppConstants.SUCCESS_MESSAGE, newPost);
return new ResponseEntity<>(successResponse, HttpStatus.OK);
}
I am using Postman for testing-
When I make the request, I get the following error (Note- I have set the error response in Spring Security as seen in the image.)
[nio-8085-exec-3] .w.s.m.s.DefaultHandlerExceptionResolver : Resolved [org.springframework.web.multipart.support.MissingServletRequestPartException: Required part 'image' is not present.]
I tried another approach but it gives another error-
#PostMapping("/uploadimage/{postid}/{isUpdatingPost}")
public ResponseEntity<SuccessResponse<String>> uploadImage(#RequestParam(name="file") MultipartFile file, #PathVariable("postid") int postid, #PathVariable("isUpdatingPost")boolean isUpdatingPost){
String result=this.postService.uploadImage(file, postid, isUpdatingPost);
SuccessResponse<String> response=new SuccessResponse<>(AppConstants.SUCCESS_CODE,AppConstants.SUCCESS_MESSAGE,result);
return new ResponseEntity<>(response,HttpStatus.OK);
}
[Request processing failed: org.springframework.web.multipart.MultipartException: Current request is not a multipart request] with root cause
org.springframework.web.multipart.MultipartException: Current request is not a multipart request
I am not able to understand the problem in these approaches. I also set the content type to multipart/form-data in Postman and the consume parameter in #PostMapping, but still getting these errors.
Please help in finding the problem!
This worked for me.
For attaching single file with #RequestParam:
#PostMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<?> uploadImage(#RequestParam MultipartFile file) throws IOException {
// ...
}
For attaching file in DTO with #ModelAttribute:
public record FileDTO(
Integer id,
MultipartFile file) {
}
#PostMapping(value = "/2", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = MediaType.IMAGE_JPEG_VALUE)
public ResponseEntity<?> uploadImage2(#ModelAttribute FileDTO dto) throws IOException {
// ...
}
BTW, you probably want use OpenAPI(Swagger UI) for manually testing your app, it's easier than using postman.
Reference article: https://www.baeldung.com/spring-file-upload
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.
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
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.
I've a Spring (3.0) Controller with a method which has HttpServletRequest as one of the parameters, since it's handling (multiple) file uploads.
#RequestMapping(value = "/classified/{idClassified}/dealer/{idPerson}/upload",
method = RequestMethod.POST)
#ResponseBody
public final String uploadClassifiedPicture(
#PathVariable int idClassified,
#PathVariable int idPerson,
#RequestParam String token,
HttpServletRequest request);
How to Unit Test it? I know I can create a MockHttpServletRequest, but I don't know how to pass one or more files to it.
MockHttpServletRequest request = new MockHttpServletRequest("POST",
"/classified/38001/dealer/54/upload?token=dfak241adf");
I recommend to change the method signature a bit, to make the uploaded file a normal parameter (of type MultipartFile (not CommonsMultipartFile)):
#RequestMapping(value = "/classified/{idClassified}/dealer/{idPerson}/upload",
method = RequestMethod.POST)
#ResponseBody
public final String uploadClassifiedPicture(
#PathVariable int idClassified,
#PathVariable int idPerson,
#RequestParam String token,
#RequestParam MultipartFile content);
Then you can use a MockMultipartFile in your test:
final String fileName = "test.txt";
final byte[] content = "Hallo Word".getBytes();
MockMultipartFile mockMultipartFile =
new MockMultipartFile("content", fileName, "text/plain", content);
uploadClassifiedPicture(1, 1, "token", mockMultipartFile);
If you do not want to change the method signature, then you can use MockMultipartHttpServletRequest instead.
It has a method addFile(MultipartFile file). And of course the required parameter can be a MockMultipartFile.
You can also use the MockMvc object as well as MockMvcRequestBuilders to send a test file upload request to your controller:
#Test
public void testSendNotEmptyFile() throws Exception {
mvc.perform(MockMvcRequestBuilders.fileUpload("Your controller URL")
.file("file", "Test Content".getBytes())
.contentType(MediaType.MULTIPART_FORM_DATA)
.accept(MediaType.APPLICATION_JSON))
.andExpect(status().isOk());
}