How to PUT multipart/form-data using Spring MockMvc?

Andremoniy picture Andremoniy · Jul 25, 2016 · Viewed 11.6k times · Source

I have a controller's method with a PUT method, which receives multipart/form-data:

   @RequestMapping(value = "/putIn", method = RequestMethod.PUT)
   public Foo updateFoo(HttpServletRequest request,
                           @RequestBody Foo foo,
                           @RequestParam("foo_icon") MultipartFile file) {
    ...
   }

and I want to test it using MockMvc. Unfortunately MockMvcRequestBuilders.fileUpload creates essentially an instance of MockMultipartHttpServletRequestBuilder which has a POST method:

super(HttpMethod.POST, urlTemplate, urlVariables)

EDIT: Surely I can I can not create my own implementation of MockHttpServletRequestBuilder, say

public MockPutMultipartHttpServletRequestBuilder(String urlTemplate, Object... urlVariables) {
    super(HttpMethod.PUT, urlTemplate, urlVariables);
    super.contentType(MediaType.MULTIPART_FORM_DATA);
}

because MockHttpServletRequestBuilder has a package-local constructor.

But I'm wondering is there any more convenient Is any way to do this, may be I missed some existent class or method for doing this?

Answer

HammerNL picture HammerNL · Nov 17, 2016

Yes, there is a way, and it's simple too!

I ran into the same problem myself. Though I was discouraged by Sam Brannen's answer, it appears that Spring MVC nowadays DOES support PUT file uploading as I could simply do such a request using Postman (I'm using Spring Boot 1.4.2). So, I kept digging and found that the only problem is the fact that the MockMultipartHttpServletRequestBuilder returned by MockMvcRequestBuilders.fileUpload() has the method hardcoded to "POST". Then I discovered the with() method...

and that allowed me to come up with this neat little trick to force the MockMultipartHttpServletRequestBuilder to use the "PUT" method anyway:

    MockMultipartFile file = new MockMultipartFile("data", "dummy.csv",
            "text/plain", "Some dataset...".getBytes());

    MockMultipartHttpServletRequestBuilder builder =
            MockMvcRequestBuilders.fileUpload("/test1/datasets/set1");
    builder.with(new RequestPostProcessor() {
        @Override
        public MockHttpServletRequest postProcessRequest(MockHttpServletRequest request) {
            request.setMethod("PUT");
            return request;
        }
    });
    mvc.perform(builder
            .file(file))
            .andExpect(status().ok());

Works like a charm!