[JAVA] [SpringBoot] So schreiben Sie einen Controller-Test

Controller-Tests sind etwas Besonderes, daher mache ich mir Sorgen. .. Notieren Sie sich, wie Sie einen Test für den geschäftlichen Spring Boot Controller schreiben.

Ziel-Controller

Ich werde einen Test für den folgenden Controller schreiben.

@Controller
@RequestMapping("/")
public class DemoController {


  /**
   * index
   */
  @RequestMapping(path = "home", method = RequestMethod.GET)
  public ModelAndView index(ModelAndView mav) {
    
    //Legen Sie einen Wert für die Nachricht fest
    mav.addObject("message", "hello world");
    mav.setViewName("index");
    return mav;
  }

  /**
   *Bildschirmanzeige eingeben
   */
  @RequestMapping(path = "form", method = RequestMethod.GET)
  public ModelAndView form(ModelAndView mav, Form form) {

    //Legen Sie den Anfangswert für den Namen des Formulars fest
    form.setName("hoge");
    mav.addObject("form", form);
    mav.setViewName("demoForm");
    return mav;
  }

  /**
   *Erhalten Sie das Ergebnis und die Validierung
   */
  @RequestMapping(path = "form", method = RequestMethod.POST)
  public ModelAndView formPost(ModelAndView mav, @Valid @ModelAttribute Form form,
      BindingResult result) {

    //Überprüfen Sie die Validierung
    if (result.hasFieldErrors()) {
      mav.addObject("errors", result.getFieldErrors());
      mav.addObject("form", form);
      mav.setViewName("demoForm");
      return mav;
    }

    //Formularwert speichern
    formService.saveData(form);

    mav.setViewName("ok");
    return mav;
  }

}

Darüber hinaus lautet die zum Senden und Empfangen von Formularen verwendete Formularklasse wie folgt. Der Name wird mit der Annotation @NotBlank überprüft.

Form.java


@Getter
@Setter
public class Form {

  @NotBlank(message = "Der Name ist ein erforderliches Element.")
  private String name;

}

Vorbereitung

Beim Testen des Spring MVC Controllers gibt es einige Versprechen. Bereiten Sie sie also zuerst vor. Ich werde den detaillierten Test in den folgenden Abschnitten beschreiben, aber ich bin beunruhigt über einen unerwarteten Fehler, wenn diese Vorbereitung nicht korrekt durchgeführt wird. (Ich habe Stunden damit verschwendet.)

Erstens, weil es notwendig ist, die DI-Funktion von Spring auch auf Junit auszuführen Fügen Sie der Testklasse die Anmerkungen @Runwith (..) und @SpringBootTest hinzu.

@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class DemoControllerTest {

Registrieren Sie die zu testende Klasse im DI-Container mit @Autowired. Bereiten Sie sich darauf vor, das Verhalten von Feder-MVC mit MockMvcBuilders.standaloneSetup (...) zu reproduzieren. Die Annotation @Before wird hinzugefügt, da sie vor jedem @Test erfolgt. Danach wird diese mockMvc-Instanz verwendet, um eine virtuelle Anforderung zu generieren und einen Test auszuführen.

  private MockMvc mockMvc;

  @Autowired
  DemoController target;

  @Before
  public void setup() {
    mockMvc = MockMvcBuilders.standaloneSetup(target).build();
  }

mockMVC wurde auf der Website hier ausführlich erläutert.

Der bisherige Code lautet wie folgt.

DemoController



@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
public class DemoControllerTest {

  private MockMvc mockMvc;

  @Autowired
  DemoController target;

  @Before
  public void setup() {
    mockMvc = MockMvcBuilders.standaloneSetup(target).build();
  }

}

Test nach Hause Methode

Schreiben Sie zunächst einen Test für die home () -Methode, der eine Ansicht zeichnet, indem Sie eine Nachricht in ein Modell mit der GET-Methode nach / home packen. Die zu testenden Punkte sind wie folgt.

Schauen wir sie uns der Reihe nach an.

Ist der HTTP-Statuscode der Antwort korrekt?

Führen Sie die Anforderung mit der Ausführung von mockMvc aus.

python


mockMvc.perform(Methodenname("Angegebene URL"))

Testen Sie dann die Antwort mit der andExcept-Methode.

python


.andExpect(Testgegenstände)

Da wir dieses Mal den HTTP-Statuscode testen, verwenden wir status (). Der Statuscode 200 kann mit status () getestet werden. IsOk. Ein typischer Statuscode kann wie folgt bestimmt werden.

Status Methode
200 status().isOk()
308 status().isPermanentRedirect()
404 status().isNotFound()
403 status().isForbidden()
503 status().isServiceUnavailable()

Der bisherige Code lautet wie folgt.

DemoControllerTest.java


  @Test
  public void getIndexTest() throws Exception {
    // when
    mockMvc.perform(get("/home"))
         .andExpect(status().isOk());
  }

Möchten Sie die angegebene Ansicht zurückgeben?

Überprüfen Sie, ob "/ home" index.html zurückgibt. Verwenden Sie view (). Name (), um die Ansicht zu beurteilen.

.andExpect(view().name(Vorlagenname))

Wenn hinzugefügt, wird es wie folgt sein.

DemoControllerTest.java


  @Test
  public void getIndexTest() throws Exception {
    // when
    mockMvc.perform(get("/home"))
        .andExpect(status().isOk())
        .andExpect(view().name("index"));
  }

Ist das Modell mit den richtigen Variablen gepackt?

Testen Sie als Nächstes den Status des Modells, um festzustellen, ob die in der Ansicht verwendeten Variablen korrekt im Modell gepackt sind. Verwenden Sie model (). Attribute (), um zu testen, ob Sie eine Variable zur Ansicht übergeben

python


model().attribute(Variablennamen,Wert)

Dieses Mal ist die Variable namens message mit hallo world gefüllt Es wird wie folgt sein.

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

Es kann andere Möglichkeiten geben, dies zu tun, aber sobald der Indextest in Ordnung ist.

Test zur formGet-Methode

Die Formularmethode packt die initialisierte formBean in ein Modell und zeigt demoForm.html an. Wenn Sie jedoch nur die Ansicht des Formulars zurückgeben, ist diese dieselbe wie zuvor. Testen Sie daher, ob Sie den Anfangswert im Namensfeld des Formulars festlegen können.

DemoController.java


    form.setName("hoge");
    mav.addObject("form", form);

Ist der Anfangswert ("hoge") für den Namen des Formulars festgelegt?

Der Inhalt des an das Modell übergebenen Objekts kann ermittelt werden, indem der Rückgabewert der Anforderung mit mockMvc.perform (). AndReturn () abgerufen wird. Empfangen Sie das Anforderungsergebnis MvcResult mit .andReturn und rufen Sie von dort aus die Ansicht und das Modell mit getModelAndView ab. Darüber hinaus wird das Modell von getModel erfasst und der Wert von "form" wird von der get-Methode erfasst. Beachten Sie, dass der Rückgabewert von get () vom Typ object ist. Lassen Sie uns ihn also mit (Form) umwandeln. Zusammenfassend sieht es wie folgt aus.

DemoControllerTest


  @Test
  public void getFormTest() throws Exception {
    // when
    MvcResult result = mockMvc.perform(get("/form"))
        .andExpect(status().isOk())
        .andExpect(view().name("demoForm"))
        .andReturn();
    //Den Wert des im Modell gepackten Formulars erhalten Sie hier
    Form resultForm = (Form) result.getModelAndView().getModel().get("form");

    // then 
    assertEquals(resultForm.getName(),"hoge");

  }

Testen der formPost-Methode

Testen Sie abschließend die formPost-Methode. Die formPost-Methode empfängt den Eingabewert des Formulars in der Post-Anfrage von demoform.html und empfängt ihn. Wenn Sie eine Validierung durchführen und keine Erroll vorliegt Es ruft FormService.saveData auf, speichert den Inhalt des Formulars und ruft ok.html auf. Da der Vorgang kompliziert ist, schreiben Sie die zu testenden Elemente auf.

Schauen wir uns jeden an.

Wenn ein Validierungsfehler vorliegt

Zuerst den Test, wenn ein Validierungsfehler vorliegt. Lassen Sie uns dazu einen Validierungsfehler auslösen. Da der Wert von name @NotBlank ist, tritt automatisch ein Fehler auf, wenn nichts angegeben wird. Fügen Sie hier explizit ein leeres Zeichen in den Namen ein. Verwenden Sie .param () oder .flashAttr, um Werte für die Anforderungsparameter einzugeben.

Im Fall von param,

// form.Beim Einfügen von Hoge in den Namen
// mockMvc.perform(post("URL-Name").param(Parametername,Wert))
 mockMvc.perform(post("/form").param("name", "hoge"))

Bei Verwendung von flashAttr

// form.Beim Einfügen von Hoge in den Namen
// mockMvc.perform(post("URL-Name").flashAttr(Parametername,Objekt))
  Form form = new Form()
  form.setName("hoge")

  mockMvc.perform((post("/form")).flashAttr("form",form))

Dieses Mal werden wir mit flashAttr testen. Ich teste, ob ein Validierungsfehler angezeigt wird, und zeige eine demoForm-Ansicht an.

Die Tatsache, dass ein Fehler aufgetreten ist, ist ...

model().hasError()

Es wird beurteilt von.

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

Wenn kein Validierungsfehler vorliegt

Testen Sie dann, ob kein Validierungsfehler auftritt. Der Artikel, den ich hier testen möchte, ist

Es gibt zwei. Ob der angegebene HTML-Code zurückgegeben werden soll, wird im vorherigen Abschnitt erläutert Beschreibt, wie Sie testen, ob Sie die angegebene Methode (formService.saveData) aufrufen.

Das erste, was zu tun ist, ist den Zieldienst zu verspotten. Verwenden Sie in springMVC @MockBean anstelle von @Mock. Sie können eine Klasse verspotten, die @Autowired ist.

Außerdem werden Klassen mit der Annotation @MockBean automatisch ausgeführt Es wird verspottet, wenn die Klasse @Autowired (in diesem Fall DemoController) in der Testklasse ausgeführt wird. Fügen wir also @MockBean vor dem @ Autowired DemoController-Ziel hinzu.

・
・
  private MockMvc mockMvc;

  //hinzufügen
  @MockBean
  FormService mockFormService;

  @Autowired
  private DemoController target;
・
・

Anschließend wird die Verifizierungsmethode von Mockit verwendet, um den Nutzungsstatus des verspotteten Objekts zu bestimmen. Die folgenden Tests zeigen, dass formService.saveData einmal mit einer Instanz namens form als Argument aufgerufen wird.

// verify(Scheinobjektname,Anzahl der Verwendungen).Methodenname ・(Streit);
verify(mockFormService, times(1)).saveData(form);

Der gesamte Testcode ist unten. In form.name wird ein Wert festgelegt, um das Auftreten eines Fehlers zu verhindern.


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

Ergebnis

Der endgültige Code ist unten. Die Abdeckung von DemoController.java beträgt jetzt sowohl für die Methode als auch für die Zeile 100%.

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

  }

}

Ich bin immer noch zuversichtlich, dass dies nicht korrekt ist. Ich hoffe, es hilft Menschen, die genauso besorgt sind, Tests zu schreiben wie sie.

Referenz

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

[SpringBoot] So schreiben Sie einen Controller-Test
So schreiben Sie einen Komponententest für Spring Boot 2
[Basic] So schreiben Sie ein Dockerfile Selbstlernend ②
[Einführung in Java] So schreiben Sie ein Java-Programm
Ich möchte einen Unit Test schreiben!
Wie schreibe ich Rails
Wie schreibe ich Docker-Compose
Wie schreibe ich Mockito
So schreiben Sie eine Migrationsdatei
Schienen: Wie man eine Rechenaufgabe schön schreibt
[Rails] Wie schreibe ich, wenn ich eine Unterabfrage mache?
So löschen Sie einen Controller usw. mit einem Befehl
JUnit 5: Wie man Testfälle in enum schreibt
So schreiben Sie Testcode mit Basic-Zertifizierung
So führen Sie die SpringBoot-App als Dienst aus
Wie man guten Code schreibt
java: Wie schreibe ich eine generische Typliste? [Hinweis]
Wie schreibe ich einen Java-Kommentar
Wie hinterlasse ich einen Kommentar?
So schreiben Sie eine Datumsvergleichssuche in Rails
Wie schreibe ich Junit 5 organisiert
Wie schreibe ich Rails Seed
So schreiben Sie ein benutzerorientiertes Programm (1)
Wie schreibe ich Rails Routing
Wie schreibe ich einen Core Mod in Minecraft Forge 1.15.2
So fügen Sie ein Video ein
So erstellen Sie eine Methode
So testen Sie eine Klasse, die application.properties mit SpringBoot verarbeitet (Anfrage: darauf hingewiesen)
Dynamisches Schreiben iterativer Testfälle mit test / unit (Test :: Unit)
Lassen Sie uns schreiben, wie API mit SpringBoot + Docker von 0 erstellt wird
So schreiben Sie eine Migration vom Rails-Datums- / Uhrzeittyp zum Datumstyp
So führen Sie einen Komponententest für Spring AOP durch
Java # 6 studieren (Wie man Blöcke schreibt)
So erstellen Sie einen Java-Container
Wie unterschreibe ich Minecraft MOD?
So erstellen Sie einen JDBC-Treiber
[Rails] Wie schreibe ich eine Ausnahmebehandlung?
[Swift] So senden Sie eine Benachrichtigung
So schreiben Sie eine Java-Variablendeklaration
So erstellen Sie einen Begrüßungsbildschirm
So erstellen Sie ein Jenkins-Plug-In
Wie erstelle ich ein Maven-Projekt?
So schreiben Sie leicht verständlichen Code [Zusammenfassung 3]
So erstellen Sie ein Java-Array
[R Spec on Rails] So schreiben Sie Testcode für Anfänger von Anfängern
So führen Sie einen Vertrag mit web3j aus
So sortieren Sie eine Liste mit Comparator