Çok parçalı POST isteğini birim test etmek için Spring MVC Testini kullanma


115

Arabaları kaydetmek için aşağıdaki istek işleyicim var. Örneğin cURL kullandığımda bunun çalıştığını doğruladım. Şimdi yöntemi Spring MVC Testiyle birim test etmek istiyorum. FileUploader'ı kullanmayı denedim, ancak onu çalıştırmayı başaramıyorum. JSON bölümünü eklemeyi de başaramıyorum.

Bu yöntemi Spring MVC Testiyle nasıl birim test edebilirim? Bununla ilgili herhangi bir örnek bulamıyorum.

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

Auto + bir veya daha fazla dosyam için bir JSON temsilini yükseltmek istiyorum.

Doğru cevaba 100 ödül ekleyeceğim!

Yanıtlar:


256

Kullanımdan MockMvcRequestBuilders#fileUploadkaldırıldığı için, MockMvcRequestBuilders#multipart(String, Object...)hangisinin a döndürdüğünü kullanmak isteyeceksiniz MockMultipartHttpServletRequestBuilder. Sonra bir grup file(MockMultipartFile)çağrıyı zincirleyin .

İşte çalışan bir örnek. Verilen bir@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;
        }

    }
}

ve bir birim testi

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

Ve @Configurationsınıf

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

Test geçmeli ve size şu çıktıyı vermelidir:

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

Unutulmaması gereken nokta, JSON'yi, farklı bir içerik türü haricinde, diğer çok parçalı dosyalar gibi gönderdiğinizdir.


1
Merhaba Sotirios, bu güzel örneği gördüğüme sevindim ve sonra bunu teklif eden kişiye baktım ve tombala! Sotirios'du! Test onu gerçekten harika kılıyor. Yine de beni rahatsız eden bir şey var, isteğin çok parçalı bir (500) olmadığından şikayet ediyor.
Stephane

AssertIsMultipartRequest (servletRequest) 'de başarısız olan bu iddiadır; CommonsMultipartResolver'ın yapılandırılmadığından şüphelendim. Ama günlükte fasulyemdeki bir kaydedici görüntüleniyor.
Stephane

@shredding Bu yaklaşımı çok parçalı bir dosya ve bir model nesnesini json olarak denetleyicime gönderirken kullandım. Ancak model nesnesi fırlatırMethodArgumentConversionNotSupportedException denetleyiciye vurulduğunda fırlıyor ... Burada kaçırdığım herhangi bir suttle adımı? - stackoverflow.com/questions/50953227/…
Brian J

1
Bu örnek bana çok yardımcı oldu. Teşekkürler
kiran

multipart, POST yöntemini kullanır. Herkes bana bu örneği ancak PATCH yöntemiyle verebilir mi?
lalilulelo_1986

16

İlkbahar MVC vitrininden alınan bu örneğe bir göz atın, bu kaynak kodunun bağlantısıdır :

@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
fileUploadlehine kullanımdan kaldırıldı multipart(String, Object...).
naXa

14

Bunun yerine yöntem MockMvcRequestBuilders.fileUploadkullanımdan kaldırılmıştır MockMvcRequestBuilders.multipart.

Bu bir örnektir:

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

İşte benim için işe yarayan şey burada, test edilen EmailController'ıma bir dosya ekliyorum. Ayrıca, verileri nasıl yayınladığıma ilişkin postacı ekran görüntüsüne de bir göz atın.

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

Postacı İsteği


Cevabınız benim için de işe yaradı çok teşekkür ederim @Alfred
Parameshwar

1

Spring4 / SpringBoot 1.x kullanıyorsanız, "text" (json) parçaları da ekleyebileceğinizi belirtmekte fayda var. Bu, MockMvcRequestBuilders.fileUpload (). Dosyası (MockMultipartFile dosyası) aracılığıyla yapılabilir (yöntem .multipart()bu sürümde mevcut olmadığından gereklidir ):

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

}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.