Utilisation de Spring MVC Test pour tester une demande POST en plusieurs parties

115

J'ai le gestionnaire de requêtes suivant pour enregistrer les autos. J'ai vérifié que cela fonctionne lorsque j'utilise par exemple cURL. Maintenant, je veux tester la méthode unitaire avec Spring MVC Test. J'ai essayé d'utiliser le fileUploader, mais je n'arrive pas à le faire fonctionner. Je n'arrive pas non plus à ajouter la partie JSON.

Comment tester l'unité cette méthode avec Spring MVC Test? Je ne suis pas en mesure de trouver d'exemples à ce sujet.

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

Je souhaite télécharger une représentation JSON pour mon auto + un ou plusieurs fichiers.

J'ajouterai 100 en prime à la bonne réponse!

Lucky Luke
la source

Réponses:

256

Comme MockMvcRequestBuilders#fileUploadest obsolète, vous voudrez utiliser MockMvcRequestBuilders#multipart(String, Object...)qui renvoie un MockMultipartHttpServletRequestBuilder. Ensuite, enchaînez un tas d' file(MockMultipartFile)appels.

Voici un exemple fonctionnel. Donné un@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;
        }

    }
}

et un test unitaire

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

Et la @Configurationclasse

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

Le test doit réussir et vous donner une sortie de

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

La chose à noter est que vous envoyez le JSON comme n'importe quel autre fichier en plusieurs parties, sauf avec un type de contenu différent.

Sotirios Delimanolis
la source
1
Salut Sotirios, j'étais heureux de voir ce bel exemple, puis j'ai regardé qui était celui qui l'offrait, et le bingo! C'était Sotirios! Le test le rend vraiment cool. J'ai une chose qui me dérange cependant, il se plaint que la demande n'est pas en plusieurs parties (500).
Stephane
C'est cette assertion qui échoue assertIsMultipartRequest (servletRequest); J'ai suspecté que CommonsMultipartResolver n'était pas configuré. Mais un enregistreur dans mon haricot est affiché dans le journal.
Stephane
@shredding J'ai adopté cette approche en envoyant un fichier en plusieurs parties et un objet modèle en tant que json à mon contrôleur. Mais l'objet modèle jette MethodArgumentConversionNotSupportedExceptionen frappant le contrôleur .. une étape de suttle que j'ai manquée ici? - stackoverflow.com/questions/50953227/…
Brian J
1
Cet exemple m'a beaucoup aidé. Merci
kiranNswamy
multipart utilise la méthode POST. Quelqu'un peut-il me donner cet exemple mais avec la méthode PATCH?
lalilulelo_1986
16

Jetez un œil à cet exemple tiré de la vitrine de Spring MVC, voici le lien vers le code source :

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

}
Université angulaire
la source
1
fileUploadest obsolète au profit de multipart(String, Object...).
naXa
14

La méthode MockMvcRequestBuilders.fileUploadest déconseillée à la MockMvcRequestBuilders.multipartplace.

Ceci est un exemple:

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
la source
2

Voici ce qui a fonctionné pour moi, ici je joins un fichier à mon EmailController en cours de test. Jetez également un œil à la capture d'écran du facteur sur la façon dont je publie les données.

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

Demande de facteur

Alferd Nobel
la source
Merci beaucoup votre réponse a fonctionné pour moi aussi @Alfred
Parameshwar
1

Si vous utilisez Spring4 / SpringBoot 1.x, il convient de mentionner que vous pouvez également ajouter des parties "text" (json). Cela peut être fait via MockMvcRequestBuilders.fileUpload (). File (fichier MockMultipartFile) (qui est nécessaire car la méthode .multipart()n'est pas disponible dans cette version):

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

}
marcheurs
la source