Używanie testu Spring MVC do testowania jednostkowego wieloczęściowego żądania POST


115

Mam następującą procedurę obsługi żądań do zapisywania automatów. Sprawdziłem, że to działa, gdy używam np. CURL. Teraz chcę przetestować metodę za pomocą testu Spring MVC. Próbowałem użyć programu fileUploader, ale nie udaje mi się go uruchomić. Nie udaje mi się też dodać części JSON.

Jak przetestować tę metodę za pomocą testu Spring MVC? Nie jestem w stanie znaleźć na to żadnych przykładów.

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

Chcę uplodować reprezentację JSON dla mojego auto + jednego lub więcej plików.

Dodam 100 premii do poprawnej odpowiedzi!

Odpowiedzi:


256

Ponieważ MockMvcRequestBuilders#fileUploadjest przestarzałe, będziesz chciał użyć, MockMvcRequestBuilders#multipart(String, Object...)który zwraca plik MockMultipartHttpServletRequestBuilder. Następnie file(MockMultipartFile)połącz kilka połączeń.

Oto działający przykład. Dawać@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;
        }

    }
}

i test jednostkowy

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

I @Configurationklasa

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

Test powinien zakończyć się pomyślnie i dać wynik

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

Należy zauważyć, że wysyłasz JSON tak jak każdy inny plik wieloczęściowy, z wyjątkiem innego typu zawartości.


1
Cześć Sotirios, ucieszyłem się, widząc ten piękny przykład, a potem spojrzałem, kto go zaoferował i bingo! To był Sotirios! Test sprawia, że ​​jest naprawdę fajnie. Martwi mnie jednak jedna rzecz, narzeka, że ​​żądanie nie jest wieloczęściowe (500).
Stephane

To jest to stwierdzenie, które zawodzi assertIsMultipartRequest (servletRequest); Podejrzewałem, że CommonsMultipartResolver nie zostało skonfigurowane. Ale logger w mojej fasoli jest wyświetlany w dzienniku.
Stephane

@shredding Przyjąłem takie podejście, wysyłając plik wieloczęściowy i obiekt modelu jako json do mojego kontrolera. Ale obiekt modelu rzuca się MethodArgumentConversionNotSupportedExceptionpo naciśnięciu kontrolera ... jakiś mały krok, który tu przegapiłem? - stackoverflow.com/questions/50953227/ ...
Brian J

1
Ten przykład bardzo mi pomógł. Dzięki
kiranNswamy

multipart używa metody POST. Czy ktoś może podać mi ten przykład, ale z metodą PATCH?
lalilulelo_1986

16

Spójrz na ten przykład zaczerpnięty z wiosennej prezentacji MVC, oto link do kodu źródłowego :

@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
fileUploadjest przestarzały na korzyść multipart(String, Object...).
naXa

14

Metoda MockMvcRequestBuilders.fileUploadjest przestarzała użyj MockMvcRequestBuilders.multipartzamiast tego.

To jest przykład:

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

Oto, co zadziałało dla mnie, tutaj załączam plik do mojego testowanego EmailController. Spójrz także na zrzut ekranu listonosza, pokazujący, jak publikuję dane.

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

Prośba listonosza


Dziękuję bardzo, twoja odpowiedź również zadziałała dla mnie @Alfred
Parameshwar

1

Jeśli używasz Spring4 / SpringBoot 1.x, to warto wspomnieć, że możesz również dodać części "text" (json). Można to zrobić za pomocą MockMvcRequestBuilders.fileUpload (). File (MockMultipartFile file) (co jest potrzebne, ponieważ metoda .multipart()nie jest dostępna w tej wersji):

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

}
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.