Menggunakan Spring MVC Test untuk menguji unit permintaan POST multi bagian


115

Saya memiliki penangan permintaan berikut untuk menyimpan mobil. Saya telah memverifikasi bahwa ini berfungsi ketika saya menggunakan misalnya cURL. Sekarang saya ingin menguji metode dengan Spring MVC Test. Saya telah mencoba menggunakan fileUploader, tetapi saya tidak berhasil membuatnya berfungsi. Saya juga tidak berhasil menambahkan bagian JSON.

Bagaimana saya menguji unit metode ini dengan Spring MVC Test? Saya tidak dapat menemukan contoh tentang ini.

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

Saya ingin meningkatkan representasi JSON untuk auto + satu atau lebih file saya.

Saya akan menambahkan 100 hadiah ke jawaban yang benar!

Jawaban:


256

Karena MockMvcRequestBuilders#fileUploadsudah tidak digunakan lagi, Anda akan ingin menggunakan MockMvcRequestBuilders#multipart(String, Object...)yang mengembalikan file MockMultipartHttpServletRequestBuilder. Kemudian rangkai banyak file(MockMultipartFile)panggilan.

Berikut adalah contoh yang berfungsi. Diberikan a@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;
        }

    }
}

dan tes unit

@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"));
    }
}

Dan @Configurationkelasnya

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

Tes harus lulus dan memberikan hasil

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

Hal yang perlu diperhatikan adalah Anda mengirim JSON seperti file multipart lainnya, kecuali dengan tipe konten yang berbeda.


1
Hai Sotirios, saya senang melihat contoh yang indah itu, dan kemudian saya melihat siapa yang menawarkannya, dan bingo! Itu adalah Sotirios! Tes membuatnya sangat keren. Saya memiliki satu hal yang mengganggu saya, ia mengeluh bahwa permintaan tersebut bukan multipart satu (500).
Stephane

Pernyataan inilah yang gagal assertIsMultipartRequest (servletRequest); Saya menduga CommonsMultipartResolver tidak dikonfigurasi. Tapi logger di kacang saya ditampilkan di log.
Stephane

@ Shredding Saya mengambil pendekatan ini dalam mengirimkan file multipart dan objek model sebagai json ke controller saya. Tetapi objek model terlempar MethodArgumentConversionNotSupportedExceptionsaat mengenai pengontrol .. ada langkah kecil yang terlewat di sini? - stackoverflow.com/questions/50953227/…
Brian J

1
Contoh ini sangat membantu saya. Terima kasih
kiranNswamy

multipart menggunakan metode POST. Adakah yang bisa memberi saya contoh ini tetapi dengan metode PATCH?
lalilulelo_1986

16

Lihat contoh ini yang diambil dari showcase MVC musim semi, ini adalah tautan ke kode sumber :

@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"));
    }

}

1
fileUploadtidak digunakan lagi karena multipart(String, Object...).
naXa

14

Sebagai gantinya, metode MockMvcRequestBuilders.fileUploadini sudah tidak digunakan lagi MockMvcRequestBuilders.multipart.

Ini contohnya:

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

    }

}

2

Inilah yang berhasil untuk saya, di sini saya melampirkan file ke EmailController saya yang sedang diuji. Lihat juga tangkapan layar tukang pos tentang cara saya memposting data.

    @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));
            }
        }

Permintaan Tukang Pos


Terima kasih banyak, jawaban Anda juga berhasil untuk saya @Alfred
Parameshwar

1

Jika Anda menggunakan Spring4 / SpringBoot 1.x, perlu disebutkan bahwa Anda juga dapat menambahkan bagian "teks" (json). Ini dapat dilakukan melalui file MockMvcRequestBuilders.fileUpload (). (MockMultipartFile file) (yang diperlukan karena metode .multipart()tidak tersedia dalam versi ini):

@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()));
   }

}
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.