javaspringmultipartform-dataspring-test-mvcmockmvc

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


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?


Solution

  • 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.multipart("/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().isOk());
    

      Works like a charm!