Le test du contrôleur est spécial, donc je suis inquiet. .. Notez comment rédiger un test pour le Spring Boot Controller, qui regorge d'affaires.
J'écrirai un test pour le contrôleur suivant.
@Controller
@RequestMapping("/")
public class DemoController {
/**
* index
*/
@RequestMapping(path = "home", method = RequestMethod.GET)
public ModelAndView index(ModelAndView mav) {
//Définir une valeur pour le message
mav.addObject("message", "hello world");
mav.setViewName("index");
return mav;
}
/**
*Affichage de l'écran d'entrée
*/
@RequestMapping(path = "form", method = RequestMethod.GET)
public ModelAndView form(ModelAndView mav, Form form) {
//Définir la valeur initiale du nom du formulaire
form.setName("hoge");
mav.addObject("form", form);
mav.setViewName("demoForm");
return mav;
}
/**
*Recevez le résultat et la validation
*/
@RequestMapping(path = "form", method = RequestMethod.POST)
public ModelAndView formPost(ModelAndView mav, @Valid @ModelAttribute Form form,
BindingResult result) {
//Vérifier la validation
if (result.hasFieldErrors()) {
mav.addObject("errors", result.getFieldErrors());
mav.addObject("form", form);
mav.setViewName("demoForm");
return mav;
}
//Enregistrer la valeur du formulaire
formService.saveData(form);
mav.setViewName("ok");
return mav;
}
}
En outre, la classe de formulaire utilisée pour l'envoi et la réception de formulaires est la suivante. Le nom est validé avec l'annotation @NotBlank.
Form.java
@Getter
@Setter
public class Form {
@NotBlank(message = "Le nom est un élément obligatoire.")
private String name;
}
Il y a des promesses dans le test du Spring MVC Controller, alors préparez-les d'abord. Je décrirai le test détaillé dans les sections suivantes, mais je suis troublé par une erreur inattendue si cette préparation n'est pas effectuée correctement. (J'ai perdu des heures dessus.)
Tout d'abord, car il est nécessaire d'exécuter également la fonction DI de Spring sur Junit Ajoutez les annotations @Runwith (..) et @SpringBootTest à la classe de test.
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class DemoControllerTest {
Enregistrez la classe à tester dans le conteneur DI avec @Autowired. Préparez-vous à reproduire le comportement de Spring MVC avec MockMvcBuilders.standaloneSetup (...). L'annotation @Before est ajoutée car elle est effectuée avant chaque @Test. Après cela, cette instance mockMvc est utilisée pour générer une requête virtuelle et exécuter un test.
private MockMvc mockMvc;
@Autowired
DemoController target;
@Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(target).build();
}
mockMVC a été expliqué en détail sur le site here.
Le code jusqu'à présent est le suivant.
DemoController
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class DemoControllerTest {
private MockMvc mockMvc;
@Autowired
DemoController target;
@Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(target).build();
}
}
Tout d'abord, écrivez un test pour la méthode home () qui dessine une vue en emballant un message dans un modèle avec la méthode GET dans / home. Les éléments à tester sont les suivants.
Regardons-les dans l'ordre.
Exécutez la requête à l'aide de l'exécution de mockMvc.
python
mockMvc.perform(Nom de la méthode("URL spécifiée"))
Ensuite, testez la réponse avec la méthode andExcept.
python
.andExpect(Articles de test)
Puisque cette fois nous testons le code d'état HTTP, nous utiliserons status (). Le code d'état 200 peut être testé avec status (). IsOk. Un code d'état typique peut être déterminé comme suit.
statut | Méthode |
---|---|
200 | status().isOk() |
308 | status().isPermanentRedirect() |
404 | status().isNotFound() |
403 | status().isForbidden() |
503 | status().isServiceUnavailable() |
Le code jusqu'à présent est le suivant.
DemoControllerTest.java
@Test
public void getIndexTest() throws Exception {
// when
mockMvc.perform(get("/home"))
.andExpect(status().isOk());
}
Vérifiez si "/ home" renvoie index.html. Utilisez view (). Name () pour juger de la vue.
.andExpect(view().name(Nom du modèle))
Une fois ajouté, ce sera comme suit.
DemoControllerTest.java
@Test
public void getIndexTest() throws Exception {
// when
mockMvc.perform(get("/home"))
.andExpect(status().isOk())
.andExpect(view().name("index"));
}
Ensuite, testez l'état du modèle pour voir si les variables utilisées dans la vue sont correctement regroupées dans le modèle. Utilisez model (). Attribute () pour tester si vous passez une variable à afficher
python
model().attribute(Nom de variable,valeur)
Cette fois, la variable appelée message est remplie avec Hello world, donc Ce sera comme suit.
python
@Test
public void getIndexTest() throws Exception {
// when
mockMvc.perform(get("/home"))
.andExpect(status().isOk())
.andExpect(view().name("index"))
.andExpect(model().attribute("message", "hello world"));
}
Il peut y avoir d'autres façons de le faire, mais une fois que le test d'index est OK pour l'instant.
La méthode form regroupe le formBean initialisé dans un modèle et affiche demoForm.html. Cependant, si vous renvoyez simplement la vue du formulaire, elle est la même qu'avant, donc testez si vous pouvez définir la valeur initiale dans le champ de nom du formulaire.
DemoController.java
form.setName("hoge");
mav.addObject("form", form);
Le contenu de l'objet passé au modèle peut être déterminé en obtenant la valeur de retour de la requête avec mockMvc.perform (). AndReturn (). Recevoir le résultat de la requête MvcResult avec .andReturn, et obtenir la vue et le modèle avec getModelAndView à partir de là, De plus, le modèle est acquis par getModel, et la valeur de "form" est acquise par la méthode get. Notez que la valeur de retour de get () est de type object, donc castons la avec (Form). En résumé, cela ressemble à ce qui suit.
DemoControllerTest
@Test
public void getFormTest() throws Exception {
// when
MvcResult result = mockMvc.perform(get("/form"))
.andExpect(status().isOk())
.andExpect(view().name("demoForm"))
.andReturn();
//Obtenez la valeur du formulaire emballé dans le modèle ici
Form resultForm = (Form) result.getModelAndView().getModel().get("form");
// then
assertEquals(resultForm.getName(),"hoge");
}
Enfin, testez la méthode formPost. La méthode formPost reçoit la valeur d'entrée de form dans la demande de publication de demoform.html et la reçoit. Si vous effectuez une validation et qu'il n'y a pas d'erroll Il appelle FormService.saveData, enregistre le contenu du formulaire et appelle ok.html. Puisque le processus est compliqué, écrivez les éléments à tester.
Regardons chacun d'eux.
Tout d'abord, le test lorsqu'il y a une validationError. Pour ce faire, lançons une validationError. Puisque la valeur de name est @NotBlank, une erreur se produira automatiquement si rien n'est spécifié. Ici, mettez explicitement un caractère vide dans le nom. Utilisez .param () ou .flashAttr pour entrer des valeurs pour les paramètres de la demande.
Dans le cas de param,
// form.Lors de la mise au nom
// mockMvc.perform(post("nom de l'url").param(Le nom du paramètre,valeur))
mockMvc.perform(post("/form").param("name", "hoge"))
Lors de l'utilisation de flashAttr,
// form.Lors de la mise au nom
// mockMvc.perform(post("nom de l'url").flashAttr(Le nom du paramètre,objet))
Form form = new Form()
form.setName("hoge")
mockMvc.perform((post("/form")).flashAttr("form",form))
Cette fois, nous allons tester avec flashAttr. Je teste si j'obtiens une erreur de validation et que je visualise une vue demoForm.
Le fait qu'une erreur s'est produite est ...
model().hasError()
Il est jugé par.
@Test
public void postFormTestInValid() throws Exception {
// given
Form form = new Form();
form.setName("");
// when
mockMvc.perform((post("/form")).flashAttr("form",form))
.andExpect(model().hasErrors())
.andExpect(model().attribute("form", form))
.andExpect(view().name("demoForm"));
}
Testez ensuite si aucune erreur de validation ne se produit. L'élément que je souhaite tester ici est
Il y en a deux. Le retour ou non du code HTML spécifié est expliqué dans la section précédente, donc Décrit comment tester si vous appelez la méthode spécifiée (formService.saveData).
La première chose à faire est de se moquer du service cible. Dans springMVC, en utilisant @MockBean au lieu de @Mock, Vous pouvez vous moquer d'une classe @Autowired.
De plus, les classes avec l'annotation @MockBean sont automatiquement Il est simulé lorsque la classe @Autowired (DemoController dans ce cas) est exécutée dans la classe de test. Ajoutons donc @MockBean avant la cible @Autowired DemoController.
・
・
private MockMvc mockMvc;
//ajouter à
@MockBean
FormService mockFormService;
@Autowired
private DemoController target;
・
・
Ensuite, utilisez la méthode verify de Mockit pour déterminer l'état d'utilisation de l'objet Mocked. Les tests suivants qui formService.saveData sont appelés une fois avec une instance appelée form comme argument.
// verify(Nom d'objet fictif,Nombre d'utilisations).Nom de la méthode ・(argument);
verify(mockFormService, times(1)).saveData(form);
Le code de test complet est ci-dessous. Une valeur est définie dans form.name pour éviter qu'une erreur ne se produise.
@Test
public void postFormTestValid() throws Exception {
// given
Form form = new Form();
form.setName("fuga");
// when
mockMvc.perform((post("/form")).flashAttr("form", form))
.andExpect(model().hasNoErrors())
.andExpect(model().attribute("form", form))
.andExpect(view().name("ok"));
// then
verify(mockFormService, times(1)).saveData(form);
}
Le code final est ci-dessous. La couverture de DemoController.java est désormais de 100% pour la méthode et la ligne.
DemoControllerTest.java
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class DemoControllerTest {
private MockMvc mockMvc;
@MockBean
FormService mockFormService;
@Autowired
private DemoController target;
@Before
public void setup() {
mockMvc = MockMvcBuilders.standaloneSetup(target).build();
}
@Test
public void getIndexTest() throws Exception {
// when
mockMvc.perform(get("/home"))
.andExpect(status().isOk())
.andExpect(view().name("index"))
.andExpect(model().attribute("message", "hello world"));
}
@Test
public void getFormTest() throws Exception {
// when
MvcResult result = mockMvc.perform(get("/form"))
.andExpect(status().isOk())
.andExpect(view().name("demoForm"))
.andReturn();
Form resultForm = (Form) result.getModelAndView().getModel().get("form");
// then
assertEquals(resultForm.getName(), "hoge");
}
@Test
public void postFormTestInValid() throws Exception {
// given
Form form = new Form();
form.setName("");
// when
mockMvc.perform((post("/form")).flashAttr("form", form))
.andExpect(model().hasErrors())
.andExpect(model().attribute("form", form))
.andExpect(view().name("demoForm"));
}
@Test
public void postFormTestValid() throws Exception {
// given
Form form = new Form();
form.setName("hoge");
// when
mockMvc.perform((post("/form")).flashAttr("form", form))
.andExpect(model().hasNoErrors())
.andExpect(model().attribute("form", form))
.andExpect(view().name("ok"));
// then
verify(mockFormService, times(1)).saveData(form);
}
}
Je suis toujours convaincu que ce n'est pas correct, J'espère que cela aidera les gens qui sont tout aussi inquiets de passer des tests qu'eux.
https://qiita.com/NetPenguin/items/0e06779ecdd48d24a5db https://ito-u-oti.com/post-129/ http://blog.okazuki.jp/entry/2015/07/14/205627 https://terasolunaorg.github.io/guideline/5.4.1.RELEASE/ja/UnitTest/ImplementsOfUnitTest/UsageOfLibraryForTest.html
Recommended Posts