Setting request parts in spring test mvc
Asked Answered
F

3

7

I'm trying to test (through Spring test (mvc)) a controller that uses servletRequest.getParts()

All I've read so far is that MockMvcRequestBuilders.fileUpload().file() is the solution. However I cannot make it work. I wrote the following test which fails

MockMultipartHttpServletRequestBuilder builder = MockMvcRequestBuilders.fileUpload("/foo")
            .file(new MockMultipartFile("file", new byte[] { 1, 2, 3, 4 }));
MockHttpServletRequest rq = builder.buildRequest(null);
Assert.assertEquals(1, rq.getParts().size()); // result 0

I went through spring code, and the call to file(...) adds an element in List<MockMultipartFile> when getParts() gets its elements from another list (Map<String, Part> parts)

There must be another way to do it...

Edit 1

The code I'm using to test the controller is :

ResultActions result = mockMvc.perform(
            MockMvcRequestBuilders.fileUpload(new URI("/url")).file("param", "expected".getBytes()))
Flyblow answered 3/5, 2016 at 9:30 Comment(3)
How are you testing this? By just calling the controller method or are you actually using MockMvc. If it is the latter you are using it wrong, if it is the former, you are making it to difficult.Chimera
I'm using MockMvc. I added the code I'm usingFlyblow
A MultipartFile isn't a Part so that won't work. You can only add parts and afaik that currently isn't possible with MockMvc. Your only change probably is to mock the Part and add it yourself by creating a custom RequestBuilder.Chimera
W
5

There is currently no support for testing with javax.servlet.http.Part in the Spring MVC Test Framework.

Consequently, I have introduced two tickets to address this shortcoming in Spring Framework 5.0:

In the interim, you should be able to mock Part yourself and register it in the prepared MockHttpServletRequest via a custom RequestPostProcessor.

Regards,

Sam (author of the Spring TestContext Framework)

Wapentake answered 5/5, 2016 at 11:47 Comment(3)
I'm not able to mock it. In RequestPartServletServerHttpRequest I run into this: this.headers = this.multipartRequest.getMultipartHeaders(this.partName); if (this.headers == null) { throw new MissingServletRequestPartException(partName); } Corrida
Hmmm... it looks like it might not be possible to get this working easily before SPR-14252 and SPR-14253 are resolved. I'll add your comments to those issues so that we take your experience into account.Wapentake
FYI: I introduced a Further Analysis section to jira.spring.io/browse/SPR-14252Wapentake
U
5

To elaborate on Sam Brannens answer: You can do it as follows:

Create a MockPart (instead of a MockMultipartFile) and add it using part() instead of file(); e.g:

MockPart coverPart = new MockPart("file", "1.png", Files.readAllBytes(Paths.get("images/1.png")));
coverPart.getHeaders().setContentType(MediaType.IMAGE_PNG);

And the use it in perform():

mockMvc.perform(multipart("/some/url")
    .part(coverPart)
    .contentType(MediaType.MULTIPART_FORM_DATA)
    .andExpect(status().isOk())

Then in you controller, you will see that request.getParts() will contain a part called "file" that you can retrieve the content from using e.g. part.getInputStream().

I used the following dependency to test this: org.springframework:spring-test:5.3.14 which is included in org.springframework.boot:spring-boot-starter-test:2.6.2

Unionist answered 5/1, 2022 at 11:30 Comment(1)
Are you considering using @RequestPart in the @RestController endpoint ? something like: @PostMapping(consumes = {MULTIPART_FORM_DATA_VALUE}) public method(@RequestPart MultipartFile file)Lynnett
H
0

Just for reference, if you have a multipart request with multiple additional json non-file request parts you can create them like the following:

ObjectMapper objectMapper = new ObjectMapper()
// ...
// a file
MockMultipartFile file = new MockMultipartFile("file", "/some/name", "text/plain", "hello".getBytes())
// and other parts
byte[] content = objectMapper.writeValueAsBytes(somePayload)
Part somePart = new MockPart("someName", content)
somePart.getHeaders().setContentType(MediaType.APPLICATION_JSON)

and then call the service like this:

mockMvc.perform(multipart("/some/path")
          .file(someFile)
          .part(somePart)
          .part(anotherPart))
       .andExpect...

Note: sending multiple MockMultiPartFiles instead of Parts works, too. But that looks ugly.

Hibbard answered 10/10, 2023 at 16:33 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.