【问题标题】:Using Spring MVC Test to unit test multipart POST request使用 Spring MVC Test 对多部分 POST 请求进行单元测试
【发布时间】:2014-03-15 01:48:30
【问题描述】:

我有以下用于保存汽车的请求处理程序。我已经验证这在我使用时有效。卷曲。现在我想用 Spring MVC Test 对该方法进行单元测试。我曾尝试使用 fileUploader,但无法使其正常工作。我也没有设法添加 JSON 部分。

我将如何使用 Spring MVC Test 对该方法进行单元测试?我找不到这方面的任何例子。

@RequestMapping(value = "autos", method = RequestMethod.POST)
public ResponseEntity saveAuto(
    @RequestPart(value = "data") autoResource,
    @RequestParam(value = "files[]", required = false) List<MultipartFile> files) {
    // ...
}

我想为我的 auto + 一个或多个文件上传 JSON 表示。

我会给正确答案加 100 赏金!

【问题讨论】:

    标签: spring unit-testing spring-mvc


    【解决方案1】:

    由于不推荐使用 MockMvcRequestBuilders#fileUpload,因此您需要使用返回 MockMultipartHttpServletRequestBuilderMockMvcRequestBuilders#multipart(String, Object...)。然后链接一堆file(MockMultipartFile) 调用。

    这是一个工作示例。给定一个@Controller

    @Controller
    public class NewController {
    
        @RequestMapping(value = "/upload", method = RequestMethod.POST)
        @ResponseBody
        public String saveAuto(
                @RequestPart(value = "json") JsonPojo pojo,
                @RequestParam(value = "some-random") String random,
                @RequestParam(value = "data", required = false) List<MultipartFile> files) {
            System.out.println(random);
            System.out.println(pojo.getJson());
            for (MultipartFile file : files) {
                System.out.println(file.getOriginalFilename());
            }
            return "success";
        }
    
        static class JsonPojo {
            private String json;
    
            public String getJson() {
                return json;
            }
    
            public void setJson(String json) {
                this.json = json;
            }
    
        }
    }
    

    还有一个单元测试

    @WebAppConfiguration
    @ContextConfiguration(classes = WebConfig.class)
    @RunWith(SpringJUnit4ClassRunner.class)
    public class Example {
    
        @Autowired
        private WebApplicationContext webApplicationContext;
    
        @Test
        public void test() throws Exception {
    
            MockMultipartFile firstFile = new MockMultipartFile("data", "filename.txt", "text/plain", "some xml".getBytes());
            MockMultipartFile secondFile = new MockMultipartFile("data", "other-file-name.data", "text/plain", "some other type".getBytes());
            MockMultipartFile jsonFile = new MockMultipartFile("json", "", "application/json", "{\"json\": \"someValue\"}".getBytes());
    
            MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build();
            mockMvc.perform(MockMvcRequestBuilders.multipart("/upload")
                            .file(firstFile)
                            .file(secondFile)
                            .file(jsonFile)
                            .param("some-random", "4"))
                        .andExpect(status().is(200))
                        .andExpect(content().string("success"));
        }
    }
    

    还有@Configuration

    @Configuration
    @ComponentScan({ "test.controllers" })
    @EnableWebMvc
    public class WebConfig extends WebMvcConfigurationSupport {
        @Bean
        public MultipartResolver multipartResolver() {
            CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver();
            return multipartResolver;
        }
    }
    

    测试应该通过并给你输出

    4 // from param
    someValue // from json file
    filename.txt // from first file
    other-file-name.data // from second file
    

    需要注意的是,您发送 JSON 的方式与发送任何其他多部分文件一样,但内容类型不同。

    【讨论】:

    • 非常好!对我来说,至关重要的是要理解答案还表明 .file 和 .param 的映射工作方式略有不同。对于 .file,3 个测试文件中的字符串“data”和“json”用于映射到 @RequestPart(value = "json") 或 @RequestParam(value = "data")。在 .param("some-random", "4") 中,"some-random" 将字符串 "4" 映射到 @RequestParam(value = "some-random")。
    【解决方案2】:

    MockMvcRequestBuilders.fileUpload 方法已弃用,请改用MockMvcRequestBuilders.multipart

    这是一个例子:

    import static org.hamcrest.CoreMatchers.containsString;
    import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
    import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
    
    import org.junit.Before;
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.mockito.Mockito;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
    import org.springframework.boot.test.mock.mockito.MockBean;
    import org.springframework.mock.web.MockMultipartFile;
    import org.springframework.test.context.junit4.SpringRunner;
    import org.springframework.test.web.servlet.MockMvc;
    import org.springframework.test.web.servlet.ResultActions;
    import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
    import org.springframework.test.web.servlet.result.MockMvcResultHandlers;
    import org.springframework.test.web.servlet.setup.MockMvcBuilders;
    import org.springframework.web.context.WebApplicationContext;
    import org.springframework.web.multipart.MultipartFile;
    
    
    /**
     * Unit test New Controller.
     *
     */
    @RunWith(SpringRunner.class)
    @WebMvcTest(NewController.class)
    public class NewControllerTest {
    
        private MockMvc mockMvc;
    
        @Autowired
        WebApplicationContext wContext;
    
        @MockBean
        private NewController newController;
    
        @Before
        public void setup() {
            this.mockMvc = MockMvcBuilders.webAppContextSetup(wContext)
                       .alwaysDo(MockMvcResultHandlers.print())
                       .build();
        }
    
       @Test
        public void test() throws Exception {
           // Mock Request
            MockMultipartFile jsonFile = new MockMultipartFile("test.json", "", "application/json", "{\"key1\": \"value1\"}".getBytes());
    
            // Mock Response
            NewControllerResponseDto response = new NewControllerDto();
            Mockito.when(newController.postV1(Mockito.any(Integer.class), Mockito.any(MultipartFile.class))).thenReturn(response);
    
            mockMvc.perform(MockMvcRequestBuilders.multipart("/fileUpload")
                    .file("file", jsonFile.getBytes())
                    .characterEncoding("UTF-8"))
            .andExpect(status().isOk());
    
        }
    
    }
    

    【讨论】:

      【解决方案3】:

      看看这个取自 Spring MVC 展示的示例,这是指向 source code 的链接:

      @RunWith(SpringJUnit4ClassRunner.class)
      public class FileUploadControllerTests extends AbstractContextControllerTests {
      
          @Test
          public void readString() throws Exception {
      
              MockMultipartFile file = new MockMultipartFile("file", "orig", null, "bar".getBytes());
      
              webAppContextSetup(this.wac).build()
                  .perform(fileUpload("/fileupload").file(file))
                  .andExpect(model().attribute("message", "File 'orig' uploaded successfully"));
          }
      
      }
      

      【讨论】:

      • fileUpload 已弃用,取而代之的是 multipart(String, Object...)
      【解决方案4】:

      这对我有用,在这里我将一个文件附加到我正在测试的 EmailController。另请查看邮递员截图,了解我如何发布数据。

          @WebAppConfiguration
          @RunWith(SpringRunner.class)
          @SpringBootTest(
                  classes = EmailControllerBootApplication.class
              )
          public class SendEmailTest {
      
              @Autowired
              private WebApplicationContext webApplicationContext;
      
              @Test
              public void testSend() throws Exception{
                  String jsonStr = "{\"to\": [\"email.address@domain.com\"],\"subject\": "
                          + "\"CDM - Spring Boot email service with attachment\","
                          + "\"body\": \"Email body will contain  test results, with screenshot\"}";
      
                  Resource fileResource = new ClassPathResource(
                          "screen-shots/HomePage-attachment.png");
      
                  assertNotNull(fileResource);
      
                  MockMultipartFile firstFile = new MockMultipartFile( 
                             "attachments",fileResource.getFilename(),
                              MediaType.MULTIPART_FORM_DATA_VALUE,
                              fileResource.getInputStream());  
                              assertNotNull(firstFile);
      
      
                  MockMvc mockMvc = MockMvcBuilders.
                        webAppContextSetup(webApplicationContext).build();
      
                  mockMvc.perform(MockMvcRequestBuilders
                         .multipart("/api/v1/email/send")
                          .file(firstFile)
                          .param("data", jsonStr))
                          .andExpect(status().is(200));
                  }
              }
      

      【讨论】:

        【解决方案5】:

        如果您使用的是 Spring4/SpringBoot 1.x,那么值得一提的是,您也可以添加“文本”(json)部分。这可以通过 MockMvcRequestBuilders.fileUpload().file(MockMultipartFile file) 来完成(因为方法.multipart() 在这个版本中不可用):

        @Test
        public void test() throws Exception {
        
           mockMvc.perform( 
               MockMvcRequestBuilders.fileUpload("/files")
                 // file-part
                 .file(makeMultipartFile( "file-part" "some/path/to/file.bin", "application/octet-stream"))
                // text part
                 .file(makeMultipartTextPart("json-part", "{ \"foo\" : \"bar\" }", "application/json"))
               .andExpect(status().isOk())));
        
           }
        
           private MockMultipartFile(String requestPartName, String filename, 
               String contentType, String pathOnClassPath) {
        
               return new MockMultipartFile(requestPartName, filename, 
                  contentType, readResourceFile(pathOnClasspath);
           }
        
           // make text-part using MockMultipartFile
           private MockMultipartFile makeMultipartTextPart(String requestPartName, 
               String value, String contentType) throws Exception {
        
               return new MockMultipartFile(requestPartName, "", contentType,
                       value.getBytes(Charset.forName("UTF-8")));   
           }
        
        
           private byte[] readResourceFile(String pathOnClassPath) throws Exception {
              return Files.readAllBytes(Paths.get(Thread.currentThread().getContextClassLoader()
                 .getResource(pathOnClassPath).toUri()));
           }
        
        }
        

        【讨论】:

          猜你喜欢
          • 2020-09-24
          • 1970-01-01
          • 2016-09-15
          • 2012-05-25
          • 2017-11-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多