Verwenden des Spring MVC-Tests zum Unit-Test einer mehrteiligen POST-Anforderung

115

Ich habe den folgenden Anforderungshandler zum Speichern von Autos. Ich habe überprüft, dass dies funktioniert, wenn ich zB cURL verwende. Jetzt möchte ich die Methode mit Spring MVC Test testen. Ich habe versucht, den fileUploader zu verwenden, aber es gelingt mir nicht, ihn zum Laufen zu bringen. Es gelingt mir auch nicht, den JSON-Teil hinzuzufügen.

Wie würde ich diese Methode mit Spring MVC Test testen? Ich kann dazu keine Beispiele finden.

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

Ich möchte eine JSON-Darstellung für meine auto + eine oder mehrere Dateien hochladen.

Ich werde 100 in Kopfgeld zur richtigen Antwort hinzufügen!

Lucky Luke
quelle

Antworten:

256

Da MockMvcRequestBuilders#fileUploades veraltet ist, möchten Sie verwenden, MockMvcRequestBuilders#multipart(String, Object...)welches a zurückgibt MockMultipartHttpServletRequestBuilder. Dann verketten Sie eine Reihe von file(MockMultipartFile)Anrufen.

Hier ist ein Arbeitsbeispiel. Angenommen@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;
        }

    }
}

und ein Unit-Test

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

Und die @ConfigurationKlasse

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

Der Test sollte bestehen und Ihnen eine Ausgabe von geben

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

Beachten Sie, dass Sie den JSON wie jede andere mehrteilige Datei senden, außer mit einem anderen Inhaltstyp.

Sotirios Delimanolis
quelle
1
Hallo Sotirios, ich freute mich, dieses schöne Beispiel zu sehen, und dann schaute ich, wer es war, der es anbot, und Bingo! Es war Sotirios! Der Test macht es wirklich cool. Ich habe eine Sache, die mich nervt, es beschwert sich, dass die Anfrage keine mehrteilige ist (500).
Stephane
Diese Behauptung schlägt fehl. AssertIsMultipartRequest (servletRequest); Ich vermutete, dass der CommonsMultipartResolver nicht konfiguriert war. Aber ein Logger in meiner Bean wird im Log angezeigt.
Stephane
@shredding Ich habe diesen Ansatz gewählt, indem ich eine mehrteilige Datei und ein Modellobjekt als json an meinen Controller gesendet habe. Aber das MethodArgumentConversionNotSupportedExceptionModellobjekt wirft, wenn es auf den Controller trifft. Gibt es einen kleinen Schritt, den ich hier verpasst habe? - stackoverflow.com/questions/50953227/…
Brian J
1
Dieses Beispiel hat mir sehr geholfen. Danke
kiranNswamy
Multipart verwendet die POST-Methode. Kann mir jemand dieses Beispiel geben, aber mit der PATCH-Methode?
lalilulelo_1986
16

Schauen Sie sich dieses Beispiel aus dem MVC-Schaufenster im Frühjahr an. Dies ist der Link zum Quellcode :

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

}
Angular University
quelle
1
fileUploadwird zugunsten von abgelehnt multipart(String, Object...).
NaXa
14

Die Methode MockMvcRequestBuilders.fileUploadist MockMvcRequestBuilders.multipartstattdessen veraltet .

Dies ist ein Beispiel:

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

    }

}
Romina Liuzzi
quelle
2

Folgendes hat bei mir funktioniert: Hier hänge ich eine Datei an meinen getesteten EmailController an. Schauen Sie sich auch den Postboten-Screenshot an, wie ich die Daten poste.

    @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 protected]\"],\"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));
            }
        }

Postbotenanfrage

Alferd Nobel
quelle
Vielen Dank, dass Ihre Antwort auch für mich funktioniert hat. @Alfred
Parameshwar
1

Wenn Sie Spring4 / SpringBoot 1.x verwenden, ist es erwähnenswert, dass Sie auch "Text" -Teile (JSON) hinzufügen können. Dies kann über die Datei MockMvcRequestBuilders.fileUpload (). (Datei MockMultipartFile) erfolgen (die benötigt wird, da die Methode .multipart()in dieser Version nicht verfügbar ist):

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

}
Walkeros
quelle