benjamin.d
benjamin.d

Reputation: 2871

Setting request parts in spring test mvc

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()))

Upvotes: 7

Views: 4155

Answers (3)

mio
mio

Reputation: 606

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.

Upvotes: 0

Per
Per

Reputation: 380

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

Upvotes: 5

Sam Brannen
Sam Brannen

Reputation: 31247

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)

Upvotes: 5

Related Questions