6

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()))
benjamin.d
  • 2,801
  • 3
  • 23
  • 35
  • 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. – M. Deinum May 03 '16 at 09:57
  • I'm using MockMvc. I added the code I'm using – benjamin.d May 03 '16 at 12:25
  • 1
    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. – M. Deinum May 03 '16 at 12:33

2 Answers2

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)

Sam Brannen
  • 29,611
  • 5
  • 104
  • 136
  • 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); } ` – Snekse Sep 02 '16 at 19:11
  • 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. – Sam Brannen Sep 03 '16 at 11:55
  • 1
    FYI: I introduced a _Further Analysis_ section to https://jira.spring.io/browse/SPR-14252 – Sam Brannen Sep 03 '16 at 12:18
2

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

Per
  • 340
  • 4
  • 9
  • Are you considering using `@RequestPart` in the `@RestController` endpoint ? something like: `@PostMapping(consumes = {MULTIPART_FORM_DATA_VALUE}) public method(@RequestPart MultipartFile file)` – manasouza Apr 12 '22 at 14:56