Ich habe den Testcode für die MVC-bezogene Verarbeitung und die Login-bezogene Verarbeitung in Spring Boot durch Ausprobieren geschrieben, daher werde ich ihn als Memorandum zusammenfassen.
Wenn Sie wissen, wie man besser schreibt, wäre ich Ihnen dankbar, wenn Sie mir dies freundlicherweise beibringen könnten (╹◡╹)
Der Quellcode ist auf GitHub verfügbar.
Dieser Artikel befasst sich mit dem Schreiben von Testcode in Spring Boot. Wenn Sie also die folgenden Punkte erfüllen, ist dies schmerzhaft.
Mit den oben genannten Annahmen können Sie durch das Lesen dieses Artikels (wahrscheinlich):
Außerdem sind diesmal die Bildschirmanzeige und der Test des JS-Teils usw. ausgeschlossen. In Zukunft habe ich vor, auch diesen Bereich abzudecken, aber plötzlich wäre es zu kompliziert, alles zu testen, also beginne ich mit einem kleinen Teil. Step up ist wichtig.
Ein einfaches Diagramm der obigen Abdeckung ist wie folgt.
Ich werde den Bereich berühren, von dem Zeitpunkt, an dem die Anfrage gesendet wird, bis zu dem Zeitpunkt, an dem View aufgefordert wird, sie anzuzeigen. Daher ist View diesmal im Grunde eine gute Nacht.
Weitere Informationen finden Sie unter GitHub pom.xml.
Spring-Boot(2.1.8)
Startereinstellungen
Testen Sie verwandte pom.xml
pom.xml(Auszug)
<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.dbunit/dbunit -->
<dependency>
<groupId>org.dbunit</groupId>
<artifactId>dbunit</artifactId>
<version>2.5.1</version>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/com.github.springtestdbunit/spring-test-dbunit -->
<dependency>
<groupId>com.github.springtestdbunit</groupId>
<artifactId>spring-test-dbunit</artifactId>
<version>1.3.0</version>
<scope>test</scope>
</dependency>
Nun wollen wir sehen, wie der Testcode von hier aus tatsächlich geschrieben wird. Da der Spring Boot-Testcode jedoch durch Kombination einiger Kenntnisse geschrieben wird, steigt der Schwierigkeitsgrad, wenn Sie versuchen, alles auf einmal abzudecken.
Daher möchte ich es in die folgenden vier Schritte unterteilen.
Level1. Teste Hello World
Level2. Datenbankoperationen testen
Level3. POST-Anfrage testen
(Stufe 4. Apps testen, für die eine Anmeldung erforderlich ist)
In Bezug auf Stufe 4 ist es notwendig, das gesamte Wissen über den in diesem Artikel verwendeten Testcode zu mobilisieren, und das Volumen wird groß sein, sodass ich es in einen anderen Artikel aufteilen werde.
Die in diesem Artikel behandelte Abdeckung der Stufen 1 bis 3 wird grob wie folgt dargestellt.
Beginnen wir mit Hello World.
Dies ist die vertraute Hallo Welt. Schauen wir uns einige gängige Prozesse an, z. B. wenn eine Anfrage an "/ hello / init" unten gestellt wird, wird "hello" als Ansichtsname zurückgegeben.
HelloController.java
@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/init")
private String init() {
return "hello";
}
}
Es ist einfach zu tun, erfordert jedoch einige neue Kenntnisse, um Testcode zu schreiben. Daher ist es ein sehr wichtiger Teil des Einstiegs in den Spring Boot-Testcode.
Erstens ist der Code selbst nicht sehr lang. Um einen Überblick zu erhalten, schreiben Sie den tatsächlichen Testcode unten.
HelloControllerTest.java
@AutoConfigureMockMvc
@SpringBootTest(classes = DbMvcTestApplication.class)
public class HelloControllerTest {
//mockMvc Mock-Objekt zur Verarbeitung von HTTP-Anforderungen und -Antworten ohne Bereitstellung auf dem Tomcat-Server
@Autowired
private MockMvc mockMvc;
//Geben Sie die Ansicht in get request an und beurteilen Sie den Erfolg / Misserfolg der Anfrage anhand des http-Status
@Test
Die Verarbeitung von void init wird ausgeführt und gibt 200 zurück() throws Exception {
// andDo(print())Anfrage / Antwort mit anzeigen
this.mockMvc.perform(get("/hello/init")).andDo(print())
.andExpect(status().isOk());
}
Als ich plötzlich eine App schrieb, fand ich viele Dinge, mit denen ich nicht vertraut war. Die Verarbeitung dieses Testcodes ist grob in "zwei" Blöcke unterteilt. Schauen wir uns also die einzelnen Blöcke genauer an.
Die der Klasse gegebenen Anmerkungen enthalten wichtige Informationen, um ein Gesamtbild des Testcodes zu erhalten. Ich benutze es normalerweise nicht, aber wenn Sie einen guten Überblick bekommen, wird der Abstand zum Testcode verkürzt.
Es ist eine Anmerkung, etwas namens "MockMvc" zu verwenden. Wer ist MockMvc?
Dies dient dazu, den physischen "Server" von der erstellten "Webanwendung" zu trennen. Es kommt nicht sehr gut heraus, also schauen wir uns an, was uns glücklich macht, indem wir es trennen.
Bei der persönlichen Entwicklung wird der Serverteil verspottet, um die Ausführungszeit des Testcodes zu verkürzen, und bei der Teamentwicklung wird der nicht beteiligte Teil verspottet und der Testcode wird geschrieben, nachdem der Einflussbereich begrenzt wurde. Ich denke, es ist besser, fortzufahren.
Das Folgende ist ein Referenzmaterial.
Japanisches Material von MockMvc Offizieller Web Layer Test
Dies ist eine Anmerkung, die sehr wichtig zu sein scheint. Es ist eigentlich eine sehr wichtige Anmerkung. Im Unit-Test von Spring Boot handelt es sich um eine Anmerkung, die fast immer angezeigt wird und viele Funktionen hat. Daher möchte ich die Funktionen Schritt für Schritt betrachten.
Die folgenden zwei Funktionen sind hier wichtig.
Lassen Sie uns zunächst über die Annotation "ExtendWith" sprechen. Die RunWith-Annotation wurde häufig in der Erläuterung des Spring Boot-Tests verwendet, aber die "RunWith-Annotation" ist eine Annotation für Junit4 und die ExtendWith-Annotation ist eine Annotation für Junit5.
Es wird für die allgemeine Implementierung der Testvorverarbeitung und -nachverarbeitung verwendet. Anschließend wird SpringExtension an die Klasse "Extension" übergeben, bei der es sich um die value-Eigenschaft handelt, und die Implementierung der allgemeinen Verarbeitung wird geschrieben. Dies ist eine evolutionäre Geschichte, daher werde ich sie diesmal weglassen, aber diese Erweiterungsklasse spielt eine wichtige Rolle, beispielsweise bei der Instanziierung eines ApplicationContext, der als DI-Container fungiert. Weitere Informationen finden Sie unter Offiziell.
Nun, wie ich schon lange geschrieben habe, ist die Verwendung eines DI-Containers mit Spring Boot nicht mehr selbstverständlich, und es ist mühsam, jedes Mal eine ExtendWith-Annotation zu schreiben. Wie Sie der Dokumentation entnehmen können, ist es selbstverständlich, sie zu verwenden. Wenn dies der Fall ist, kann es eingeschlossen werden, sodass nur die SpringBootTest-Annotation in Ordnung ist.
Durch das Erstellen einer Anmerkung, die auf diese Weise mehrere Funktionen enthält, ist es möglich, die Beschreibung der Prämisse des Testcodes zu vereinfachen. Dieses Mal werden wir jedoch die Verständlichkeit für die Integration hervorheben Anmerkungen werden weggelassen.
Schauen wir uns als nächstes die ApplicationContext-Einstellungen an. ApplicationContext wird oft als Wort verwendet, aber ich denke, es ist in Ordnung, wenn Sie es als "DI-Container" betrachten.
Es ist nicht möglich, alles durch einfaches Bestehen der Hauptklasse zu lösen, aber ich möchte dies im Bereich des Testens auf der Dao-Ebene ansprechen, für die keine HTTP-Anforderung / Antwort erforderlich ist.
Die Erklärung ist nur durch das Einstellen lang geworden, aber sobald Sie sie verstanden haben, ist sie ein wichtiger Teil, der beim Schreiben anderer Testcodes verwendet werden kann. Daher kann es sinnvoll sein, sich hinzusetzen und zu studieren. .. (Ich habe verstanden, dass ich DI überhaupt nicht verstanden habe, daher war es eine gute Gelegenheit, Spring zu überprüfen.)
Endlich bin ich zum eigentlichen Testcode gekommen. Es ist einfacher und macht mehr Spaß als die bisherigen detaillierten Einstellungen. Nachdem es eine Lücke gibt, schauen wir uns noch einmal den Teil an, der sich auf den Testcode bezieht.
HelloControllerTest.java(Auszug)
//Geben Sie die Ansicht in get request an und beurteilen Sie den Erfolg / Misserfolg der Anfrage anhand des http-Status
@Test
Die Verarbeitung von void init wird ausgeführt und gibt 200 zurück() throws Exception {
// andDo(print())Anfrage / Antwort mit anzeigen
this.mockMvc.perform(get("/hello/init")).andDo(print())
.andExpect(status().isOk());
}
Der obige Testcode führt zwei Prozesse aus, "Ausführung der Anforderung" und "Überprüfung der Antwort".
Beide Prozesse werden basierend auf der Instanz von "MockMvc" ausgeführt und im Wesentlichen in einer Anweisung wie oben beschrieben beschrieben. Indem Sie sie in einem Satz zusammenfassen, können Sie in Form eines englischen Satzes herausfinden, was der Test tut. Mal sehen, wie es tatsächlich geschrieben ist.
Um das Obige zusammenzufassen, lautet der englische Text wie folgt. (Ich kann nicht sehr gut schreiben, also fühle es bitte in der Atmosphäre ƪ (˘⌣˘) ʃ)
Perform get request to [/hello/init] and print the result, and I expect that status is 200 OK.
Da die Programmiersprache in Englisch geschrieben ist, besteht natürlich der Vorteil, dass englische Muttersprachler Testcode in einer Struktur lesen und schreiben können, die den gewohnten englischen Sätzen ähnelt. (Es scheint keine Möglichkeit zu geben, dem Englischen zu entkommen, daher kann es nicht schmerzhaft sein, wenn Sie zumindest so lesen und zuhören, wie es ist.)
Es ist ein wenig abseits des Themas, aber wenn Sie die Testmethode und die erwarteten Ergebnisse in einem einfach zu lesenden Format beschreiben, können Sie leicht wichtige Informationen aus dem Testcode abrufen, um die Systemspezifikationen und den tatsächlichen Quellcode zu verstehen. Werden. Wenn Sie es umdrehen, wird Testcode, der nicht den Spezifikationen entspricht, den Fehler übersehen und nicht zum Verständnis der Spezifikationen führen, sodass es sich um nutzlosen Code handelt.
In einer so einfachen Codephase kann es sinnvoll sein, sich daran zu gewöhnen, Testcode zu schreiben, während man sich immer darüber im Klaren ist, "was als Funktion im implementierten Prozess realisiert werden sollte".
Nach langem Hin und Her lautet die Anforderungsantwort, wenn HelloWorld mit dem Testcode überprüft wird, wie folgt.
Sie sollten bestätigen können, dass die oben beschriebenen Inhalte erfüllt sind.
Schließlich kann ich bestätigen, dass Hello World ordnungsgemäß funktioniert. Ich habs gemacht.
Da es eine Grenze gibt, was allein mit dem HelloWorld-Testcode überprüft werden kann, möchte ich die Modellüberprüfung als eine weitere praktische Sache auf Stufe 1 betrachten.
Das Modell bezieht sich hier auf "Java-Objekt, auf das von View verwiesen wird" und wird häufig in "Model.addAttribute" usw. geschrieben. Es kommt sehr häufig vor, dass ich dachte, ich würde den Wert gut in das Modell einfügen, aber er war nicht darin, sodass ich den Server nicht starten und mit einem Klick auf den Bildschirm zugreifen musste ... Es wäre sehr praktisch, wenn wir überprüfen könnten, ob es funktioniert.
Im Folgenden erfahren Sie, wie Sie den Inhalt des Modells mit Testcode überprüfen können. Der Inhalt ist leichter zu verstehen als die, die ich bisher angesprochen habe. Ich hoffe, Sie beherrschen die Verwendung.
Schauen wir uns zunächst den zu testenden Code an. Das heißt, es ist nur eine kleine Erweiterung von HelloController, und es ist nicht so schwierig, also werde ich hier alles auf einmal setzen.
HelloController.java
@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping("/init")
private String init(Model model) {
//Benutzerliste Zuerst manuell generieren
List<User> userList = new ArrayList<User>();
User user = new User();
user.setUserId(0L);
user.setUserName("test0");
User user2 = new User();
user2.setUserId(1L);
user2.setUserName("test1");
userList.add(user);
userList.add(user2);
//Legen Sie eine Liste der Benutzer im Formular fest und fügen Sie sie dem Modell hinzu, um die Grundlage für die Überprüfung zu legen, ob sie erfolgreich zum Modell hinzugefügt wurde.
DbForm form = new DbForm();
form.setUserList(userList);
model.addAttribute("message", "hello!");// 1
model.addAttribute("user", user);// 2
model.addAttribute("dbForm", form);// 3
return "hello";
}
}
Im Folgenden folgen wir jedem Muster für model.addAttribute
.
Beginnen wir mit einem einfachen Beispiel.
In model.addAttribute (" message "," hello! ");
Speichert das Modell einfach "message" als Schlüssel und "hello" als Wert.
Der Testcode, um dies zu überprüfen, lautet wie folgt.
HelloControllerTest.java(Auszug)
@Test
Hallo wird im void init-Prozess an die Modellnachricht übergeben() throws Exception {
this.mockMvc.perform(get("/hello/init"))
.andExpect(model().attribute("message", "hello!"));
}
Der Testcode ist auch sehr einfach, und wie Sie aus dem Teil `andExpect (model (). Attribute (" message "," hello! "))" Ersehen können, ist es fast dasselbe wie das Einfügen in das eigentliche Modell. Sie können auch Testcode schreiben.
Wenn Sie sich das Ergebnis tatsächlich ansehen, können Sie sehen, dass das Modellteil korrekt mit Werten gepackt ist.
Wenn es sich um ein einfaches Objekt handelt, kann es einfach geschrieben werden, da die Eigenschaft nur eine Ebene hat. Wenn Sie sich die Ergebnisse jedoch genauer ansehen, werden Sie feststellen, dass der Wert in eine zweifelhafte Zeichenfolge geschrieben ist. Es repräsentiert die Instanz des Objekts selbst. Natürlich gibt es Zeiten, in denen Sie überprüfen möchten, ob ein Objekt nicht null ist, aber meistens möchten Sie wissen, ob eine bestimmte Eigenschaft im Objekt den erwarteten Wert hat.
Im Folgenden wird die Validierung eines Modells mit diesen verschachtelten Eigenschaften beschrieben.
Das Überprüfen verschachtelter Objekte macht den Testcode etwas komplizierter, ist jedoch viel einfacher als das Interpretieren von Anmerkungen. Lassen Sie uns also jedes einzelne untersuchen.
Überprüfen Sie hier in Bezug auf die Verarbeitung von model.addAttribute (" user ", user);
, ob die Eigenschaft "userName" der "user" -Instanz wie erwartet ist (hier der Wert "test0"). Gehen.
Beginnen wir mit dem eigentlichen Testcode.
HelloControllerTest.java(Auszug)
@Test
Die Benutzerentität wird durch ungültige Init-Verarbeitung im Modell gespeichert() throws Exception {
this.mockMvc.perform(get("/hello/init"))
.andExpect(model()
.attribute("user", hasProperty(
"userName", is("test0")
)
)
);
}
Plötzlich änderte sich die Struktur drastisch. Dies ist nicht der Testcode von Spring Boot, sondern die Verarbeitung durch das Framework, das den sogenannten "Matcher" verarbeitet, der die Gültigkeit des Tests mit dem Namen "Hamcrest" überprüft.
Die Methode hasProperty
zum Überprüfen der Eigenschaften eines Objekts wird beim statischen Import verwendet, daher lautet die Struktur genau HasPropertyWithValue.hasProperty
.
Und diese Methode hat die folgenden Rollen. (Zitiert vom Beamten)
Creates a matcher that matches when the examined object has a JavaBean property with the specified name whose value satisfies the specified matcher.
Irgendwie kam ein esoterischer englischer Satz heraus, aber ich denke, dass es gut kommen wird, wenn Sie sich ein aktuelles Beispiel ansehen.
assertThat(myBean, hasProperty("foo", equalTo("bar"))
Dies bedeutet, dass das Objekt "myBean" eine Eigenschaft namens "foo" hat und der Wert der foo-Eigenschaft "bar" ist. Genau das möchten wir in diesem Test überprüfen.
Ein einfaches Beispiel für dieses Beispiel wäre assertThat (user, hasProperty (" userName ", is (" test0 "));
.
Da der Rückgabewert von hasProperty zum Matcher-Typ gehört, können Sie auch verschachtelte Eigenschaften schreiben.
Die Überprüfung verschachtelter Eigenschaften verlängert zwangsläufig den Code und macht es schwierig, die Entsprechung zwischen Klammern zu verstehen. Daher denke ich, dass einige Maßnahmen erforderlich sind, um das Lesen zu erleichtern, z. B. das Entwickeln von Einrückungen wie im obigen Beispiel.
Auf diese Weise können komplizierte Modelle gehandhabt werden. Schauen wir uns als Beispiel für das endgültige Modell den Testcode für das List-Objekt an.
Schauen wir uns das endgültige Modellmuster an, das verschachtelt ist und eine Listenstruktur hat. Stellen Sie als allgemeines Beispiel für "model.addAttribute (" dbForm ", Formular);" sicher, dass die "Liste der Benutzer im Formularobjekt" Ihren Erwartungen entspricht. Schreiben Sie als Beispiel zuerst den folgenden Code.
HelloControllerTest.java(Auszug)
//Greifen Sie bei Listenelementen mit hasItem in beliebiger Reihenfolge auf die Liste zu und überprüfen Sie, ob es ein Element gibt, dessen angegebene Eigenschaft den angegebenen Wert hat.
//Machen Sie den Test nur grün, wenn er existiert
@Test
Die Benutzerliste wird in der Modellform durch ungültige Init-Verarbeitung gespeichert() throws Exception {
this.mockMvc.perform(get("/hello/init"))
.andExpect(model().attribute("dbForm", hasProperty(
"userList", hasItem(
hasProperty(
"userName", is("test1")
)
)
)));
}
Eine neue Methode namens "hasItem" ist erschienen. Offiziell Es wird gesagt, dass es nur für Objekte im Listenformat verwendet werden kann. Ich bin. Und die hier verwendete Methode hat Matcher als Argument.
Mit anderen Worten, grob gesagt, überprüfen wir, ob es mindestens einen gibt, der den Matcher erfüllt, der als Argument in der Liste übergeben wurde, die von der hasItem-Methode ausgeführt werden soll. In diesem Beispiel möchten wir überprüfen, ob für jedes Benutzerelement in der Benutzerliste mindestens eines vorhanden ist, dessen Eigenschaft "userName" auf "test1" festgelegt ist.
Da die Liste im Beispiel eine kleine Anzahl von Elementen wie "2" enthält, ist es möglich, den gesamten Inhalt zu untersuchen. In der tatsächlichen Anwendung wird jedoch häufig eine Liste mit Hunderten oder Tausenden von Elementen übergeben. .. Es ist schwer, dies alles zu überprüfen, obwohl es codiert werden kann. In einem solchen Fall scheint die Zuverlässigkeit in gewissem Maße gewährleistet zu sein, wenn überprüft werden kann, ob die Elemente am Anfang, in der Mitte und am Ende den Spezifikationen entsprechen. Anstatt alle Elemente der Liste zu überprüfen, müssen daher nur einige als repräsentative Elemente überprüft werden. Ich denke, es wäre besser, einige Testfälle mit der hasItem-Methode bereitzustellen.
Übrigens ist es ziemlich lang geworden, weil es mit verschiedenen ergänzenden Erklärungen gemischt wurde, aber dies vervollständigt die Überprüfung des Testcodes der Stufe 1. Wenn Sie Level 1 abgeschlossen haben, können Sie Folgendes tun:
Als nächstes möchte ich auf Ebene 2 die Überprüfung der Datenbank betrachten, die den Kern der Anwendung bildet.
Im Komponententest mit Spring Boot wird die Datenbank mit "DbUnit" überprüft. Wenn Sie es so schreiben, obwohl Spring Boot alleine voll ist, müssen Sie mehr lernen ... aber ich denke, es reicht aus, um zu lernen, wie man DbUnit einfach benutzt. Das Wichtigste ist, sich darüber im Klaren zu sein, "welche Art von Arbeit einfacher sein wird", indem DbUnit und Spring Boot kombiniert werden.
Wenn Sie plötzlich in einem Durcheinander über DbUnit schreiben, ist das Bild schwer zu verstehen. Lassen Sie uns zunächst den Ablauf verfolgen, wie Datenbankoperationen von manuellen Tests zu automatisierten Tests ersetzt werden.
Betrachten Sie zunächst das manuelle Testen Ihrer Datenbankoperationen. Ich denke, dass der Test im folgenden Ablauf fortgesetzt wird.
Führen Sie den Prozess aus, um den Datensatz von SELECT aus der Datenbank auf der Anwendungsseite abzurufen
Stellen Sie sicher, dass das Akquisitionsergebnis wie erwartet ist, während Sie den erfassten Datensatz betrachten
Führt den Prozess des UPDATE und DELETE von Datenbankeinträgen auf der Anwendungsseite aus
Vergleichen Sie die Datensätze in der Datenbank vor und nach dem Anwenden des Prozesses und stellen Sie sicher, dass die Ergebnisse den Erwartungen entsprechen.
Die Vor- und Nachteile des manuellen Testens bleiben hier, aber hier konzentrieren wir uns auf die "Testreproduzierbarkeit". Wenn Sie sich als Team entwickeln, ändert sich das Ergebnis von SELECT von Moment zu Moment, und die Verarbeitung von UPDATE und DELETE erfordert einige Vorbereitungen, wenn Sie dieselben Bedingungen erfüllen möchten. Wenn die durchgeführten Tests nicht reproduzierbar sind, ist es schwierig festzustellen, ob tatsächlich eine Verschlechterung aufgetreten ist, wenn wiederholte Tests wie Refactoring- und Regressionstests durchgeführt werden.
Lassen Sie uns nun etwas mehr über manuelle Tests entwickeln.
Um die Reproduzierbarkeit zu gewährleisten, habe ich versucht, den folgenden Prozess in den obigen manuellen Test einzubeziehen.
Dies stellte die Reproduzierbarkeit des Tests sicher. Wenn dies der Fall ist, können Sie mit Vertrauen testen ... !! Es ist möglicherweise nicht möglich, wenn es sich um eine kleine Anwendung handelt. Bei jeder kleinen Überprüfung können Sie jedoch die gesamte Datenbank sichern, die gesamte Datenbank löschen, die gesamte Datenbank wiederherstellen usw. Es wird sehr viel Zeit in Anspruch nehmen.
Selbst wenn Sie einen reproduzierbaren Teststatus erstellen und der Test selbst lange dauert oder das Ergebnis zurückgibt, wird der Test nicht zum richtigen Zeitpunkt ausgeführt. Ich wollte nur ein bisschen debuggen, aber wenn ich nicht gut genug wäre, müsste ich zehn Minuten warten, bevor die Ergebnisse zurückkommen, und wenn ich nicht gut genug wäre, würde es verschoben und ich würde zum manuellen Tick-Test zurückkehren.
Bisher scheint es eine entmutigende Aufgabe zu sein, Datenbankoperationen zu testen und gleichzeitig die Reproduzierbarkeit sicherzustellen. Durch Kombinieren von Spring Boot und DbUnit können Sie den obigen Test jedoch per Knopfdruck ausführen, obwohl einige Vorbereitungen erforderlich sind.
Schauen wir uns nun den Testcode mit Spring Boot und DbUnit als Hauptthema von Level 2 an.
Schauen wir uns zunächst den Prozess an, bei dem das Ergebnis mit der königlichen Straße SELECT erzielt wird. Der Code der zu verifizierenden Dao-Schicht wird unten beschrieben.
UserDao.java(Auszug)
/**
*Holen Sie sich alle Benutzerdatensätze aus der Datenbank
*Diesmal wurde der Prozess zum Testen vereinfacht.
* @return Liste der Benutzerentitäten
*/
public List<User> findAllUser() {
QueryBuilder query = new QueryBuilder();
query.append("select user_id, user_name from tm_user");
return findResultList(query.createQuery(User.class, getEm()));
}
Es werden verschiedene Prozesse geschrieben, aber die folgenden zwei Punkte sollten beachtet werden.
Das Auswählen von Datensätzen aus der Datenbank wird weiterhin wiederholt. Der erste Schritt besteht darin, die Anzahl der Datensätze in der Datenbank = Listengröße zu überprüfen.
Ich werde DbUnit sofort zur Überprüfung verwenden, aber einige Vorbereitungen sind erforderlich, bevor der eigentliche Testcode geschrieben wird. Es gibt eine Menge Dinge zu tun, um sich vorzubereiten, aber sobald Sie es beherrschen, können Sie es zu einer Routine für nachfolgende Datenbankbetriebstests machen, daher möchte ich genauer hinsehen.
Zunächst legen wir den Grundstein für die Verwaltung von Datenbankeinträgen in Dateien. Als Standardfunktion von DbUnit werden Datensatztransaktionseinstellungen usw. in einer XML-Datei beschrieben. Dieses Mal werden jedoch Datensätze mit CSV verwaltet. Es gibt verschiedene Gründe, aber zusammenfassend ist die große Sache, dass Sie einfach schreiben können.
Ich werde im Folgenden einige Schritte ausführen, aber alle sind einfach, sodass Sie sie meiner Meinung nach bis zu einem gewissen Grad intuitiv verstehen können, ohne zu weit in DbUnit selbst zu gehen.
Zunächst erstellen wir eine Klasse, um die CSV-Datei zum Testen zu verwenden.
CsvDataSetLoader
CsvDataSetLoader.class
public class CsvDataSetLoader extends AbstractDataSetLoader{
@Override
protected IDataSet createDataSet(Resource resource) throws Exception {
return new CsvURLDataSet(resource.getURL());
}
}
Das Folgende ist eine ergänzende Erklärung der wichtigen Elemente.
Wörtlich eine abstrakte Klasse zum Lesen eines Datensatzes. Der Datensatz steht hier für "eine Reihe von Tabellen". Da diese abstrakte Klasse eine Implementierungsklasse der DataSetLoader-Schnittstelle ist, ist die zu erstellende Klasse vom Typ "DataSetLoader". Mit anderen Worten, wenn Sie sich die auf Klassenebene erstellte Klasse ansehen, ist dies so einfach wie die Beschreibung der Informationen "Dies ist eine Klasse zum Lesen eines Datensatzes".
Wie der Name schon sagt, handelt es sich um eine Factory-Methode zum Erstellen von Datasets. Das als Argument übergebene Objekt vom Ressourcentyp "resouce" enthält Informationen und Verhalten für den Zugriff auf die "echte Datei". Im eigentlichen Test hat das Ressourcenobjekt die Form, den Pfad der zu verarbeitenden CSV-Datei zu speichern.
In Official Diese Klasse erstellt ein IDataSet mit einer Basis-URL, die CSV-Dateien enthält
Wie Sie sehen können, kann DbUnit die eigentliche CSV-Datei verarbeiten, indem sie sie auf der Grundlage des oben genannten Ressourcenobjekts abruft und in ein Dataset-Objekt konvertiert.
Einige Verarbeitungen wurden geschrieben, aber wie der Klassenname andeutet, dient diese Klasse zum Lesen der eigentlichen CSV-Datei und zum Bereitstellen zum Testen von Datenbankoperationen.
Einmal geschrieben, kann es beim Testen von Datenbankvorgängen mit CSV-Dateien in anderen Apps verwendet werden. Wenn Sie hier also einen Überblick über die einzelnen Prozesse erhalten, liegt das Problem darin. Ich denke nicht.
Nachdem die Klasse zum Lesen von CSV abgeschlossen wurde, erstellen wir eine CSV-Datei, die tatsächlich gelesen werden soll. Eine Beispieldatei finden Sie unter GitHub.
Es gibt verschiedene Möglichkeiten, die CSV-Datei selbst zu erstellen. Ich persönlich empfehle jedoch, DBeaver zu verwenden, um die CSV-Datei aus dem Datensatz zu extrahieren, da sie unverändert verwendet werden kann. Beachten Sie beim Erstellen einer CSV-Datei die folgenden Punkte.
Sie müssen auch vorsichtig sein, wo Sie Ihre CSV-Dateien ablegen. (Ich habe es an einen seltsamen Ort gebracht und war süchtig.) Grundsätzlich wird es unter "src / test / resources" platziert. Referenz
Die spezifische Datei- / Ordnerstruktur ist wie folgt.
Sie können sehen, dass es eine CSV-Datei gibt, die wie ein Tabellenname unter "src / test / resources / testData" aussieht. Und daneben befindet sich eine unbekannte Textdatei namens "table-ordering.txt". Dies dient dazu, externe Schlüsselbeschränkungen zu vermeiden, und gibt die Reihenfolge an, in der die Datenbanktabellen gelesen werden. Die spezifische Schreibmethode ist wie folgt.
table-ordering.txt
TableA
TableB
TableC
Jetzt, da wir endlich fertig sind, können wir in den Testcode einsteigen. Die Anzahl der Anmerkungen wird plötzlich zunehmen, aber wenn Sie dies überwinden, wird sich der Bereich, in dem Sie Tests schreiben können, dramatisch erweitern, sodass ich mein Bestes geben werde.
DBSelectTest.java
@DbUnitConfiguration(dataSetLoader = CsvDataSetLoader.class)
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
@SpringBootTest(classes = {DaoTestApplication.class}, webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class DBSelectTest {
@Autowired
private UserDao userDao;
//Durch Setzen des Werts von DatabaseSetup auf den Pfad der CSV-Datei "table"-ordering.Siehe txt "
//Erstellen Sie eine Testtabellengruppe, indem Sie eine sequentielle Tabelle erstellen
//Zu diesem Zeitpunkt lautet der Wertpfad "src"./test/Ausgehend von "Ressourcen" lautet der Dateiname der CSV-Datei
//Entspricht dem Namen der Tabelle
//
//Ebenfalls,@Durch Hinzufügen der Transaktionsanmerkung können Sie die Transaktion nach Abschluss des Tests zurücksetzen.
//Kann getestet werden, ohne die Umwelt zu verschmutzen
@Test
@DatabaseSetup(value = "/testData/")
@Transactional
public void contextLoads() throws Exception {
List<User> userList = userDao.findAllUser();
//Hat Dao erfolgreich Datensätze aus der Tabelle abgerufen?
assertThat(userList.size(), is(2));
}
}
Beginnen wir mit den Anmerkungen, die der Klasse gegeben wurden, um einen Überblick zu erhalten.
DbUnitConfiguration
Wie Sie lesen können, handelt es sich um eine Anmerkung zum Festlegen verschiedener Einstellungen von DbUnit. Durch Angabe des oben in der Eigenschaft "dataSetLoader" erstellten "CsvDataSetLoader" kann die CSV-Datei gelesen werden. Es scheint, dass es verschiedene andere Einstellungen gibt, aber in diesem Stadium denke ich, dass es kein Problem gibt, zu erkennen, dass es zum Laden von CSV verwendet wird.
TestExecutionListeners
Dies ist der schwierigste Teil dieses Testcodes. Ich denke, diese Anmerkung sollte ausreichen, um Ihnen einen Überblick zu geben und zu organisieren, was an die Immobilie weitergegeben werden soll. Als Übersicht dient es zum Laden des erforderlichen TestExecutionListener, der die vor und nach der Ausführung des Testcodes auszuführende Verarbeitung definiert.
Die Beschreibung jedes Listeners ist grob in Official zusammengefasst. Hier werde ich kurz diejenigen beschreiben, die oft verwendet werden.
Geben Sie bei Verwendung von DI im Testcode an. Durch Angabe ist es möglich, die Testzielklasse mit Autowired usw. aus dem DI-Container zu injizieren.
Geben Sie an, wann Sie die Transaktion für den DB-Vorgang festlegen. Nach dem Betrieb der Datenbank ist es grundlegend, die Datenbank in ihren ursprünglichen Zustand zurückzusetzen. Daher ist sie für Tests, die die Datenbank verarbeiten, grundsätzlich unerlässlich.
Geben Sie an, wann Sie den Status der Datenbank vor und nach dem Test mit der später beschriebenen Anmerkung festlegen möchten. Wie der Name schon sagt, fügen Sie DbUnit bei Verwendung grundsätzlich hinzu.
SpringBootTest
Nun, dies ist der zweite Auftritt. Hier wird eine neue Eigenschaft "webEnvironment" übergeben. "MOCK" ist als Standardwert festgelegt, und es scheint, dass Sie eine sogenannte "Mock-Servlet-Umgebung" erstellen. Die Details wurden nicht viel offiziell geschrieben, aber was die Konsolenausgabe betrifft, scheint sie den Prozess der Generierung des DispatcherServlet für Tests darzustellen, die von MockMvc verwendet werden.
Da es nicht erforderlich ist, Anforderungen und Antworten nur mit dem Server in der Dao-Schicht auszutauschen, setzen Sie "NONE" als Eigenschaftswert. Dies verkürzt den Prozess der Erstellung eines Kontexts für MockMvc, wodurch der Test etwas schneller beendet wird.
Und obwohl die Reihenfolge umgekehrt wurde, hat sich auch geändert, was an die class-Eigenschaft übergeben wird. Dies bedeutet nicht, dass sich der Prozess erheblich geändert hat, sondern schränkt lediglich den Umfang der Komponentenscans ein. Wenn Sie sich den tatsächlichen Code ansehen, wird er zu Ihnen kommen.
DaoTestApplication.java
@EntityScan("app.db.entity")
@SpringBootApplication(scanBasePackages = "app.db.dao")
public class DaoTestApplication {
public static void main(String[] args) {
SpringApplication.run(DaoTestApplication.class, args);
}
}
Das zu lesende Ziel ist auf "Dao-Ebene" und "Von Dao behandelte Entität" beschränkt. Mit zunehmender Größe der App steigt auch die Zeit, die zum Laden des Pakets benötigt wird, wodurch es zeitaufwändiger wird. Da die Verarbeitung in Bezug auf Datenbankoperationen häufig geändert wird und wir den Testcode ständig ausführen möchten, haben wir den Lesebereich minimiert, um die Zeit so weit wie möglich zu verkürzen.
Mit dieser Art von Einfallsreichtum wird der Test nach dem Dehnen in etwa einer Umdrehung des Halses abgeschlossen. Wenn Sie mehr Geschwindigkeit wünschen, müssen Sie mit der Config-Klasse und mit EntityManager herumspielen, aber das Testen ist nicht so tödlich langsam. Gehen Sie also nicht so weit.
Die oben genannten Einstellungen können schnell vorgenommen werden, und der Effekt kann so wie er ist erzielt werden. Ich denke, dass dies in der Grundphase ausreicht.
Es geht wieder um Anmerkungen, aber es ist einfacher als Anmerkungen auf Klassenebene, daher denke ich, dass es Ihnen in den Sinn kommen wird.
DatabaseSetup
Dies ist eine Anmerkung, die den "Anfangszustand" der Datenbank definiert. Wenn Sie das Verzeichnis angeben, in dem sich die CSV-Datei in der value-Eigenschaft befindet, werden die Werte basierend auf der CSV-Datei in die Datenbanktabelle gepackt. Sie können auch verschiedene Status erstellen, indem Sie die Verzeichnisse wechseln.
Auf diese Weise können Sie den Status der Tabelle immer neu erstellen, wenn Sie den Test starten möchten, ohne manuell in die Tabelle einfügen zu müssen. Vielen Dank.
Transactional
Es ist eine vertraute, die häufig in der tatsächlichen Anwendungsentwicklung verwendet wird. Normalerweise verhält sich eine Methode mit dieser Annotation wie ein Commit, wenn sie normal funktioniert, ein Rollback, wenn etwas Unerwartetes ausgeführt wird, und so weiter.
Im Fall von Testcode geht jedoch die Reproduzierbarkeit des Tests verloren, wenn die Datenbank nach jedem Test neu geschrieben wird. Daher wird sie standardmäßig bei jeder Ausführung der Methode zurückgesetzt.
Durch Kombinieren der beiden oben genannten Anmerkungen können Sie die Datenbank manuell sichern, Datensätze aus Dateien einfügen, am Ende wieder einfügen usw.
Wenn Sie den Test tatsächlich ausführen, können Sie die Liste der Benutzerentitäten mit der Dao-Methode abrufen und überprüfen, ob Sie das erwartete Ergebnis erhalten (Listengröße = Anzahl der Datensätze).
Durch die Überprüfung des SELECT-Prozesses haben wir die Grundlagen des Testcodes für Datenbankoperationen in gewissem Umfang behandelt, sodass ich mir andere Prozesse gleichzeitig ansehen möchte.
Bei der CRUD-Verarbeitung konnten wir SELECT überprüfen. Schauen wir uns also die verbleibende Aktualisierungs- / Erstellungsverarbeitung an. Ich denke, dass Sie die Gliederung mit dem Wissen verstehen können, das Sie bisher erworben haben, so dass der tatsächliche Testcode unten beschrieben wird.
CRUDDaoTest.java(Auszug)
//Verarbeitung, um den Status nach Ausführung der Testmethode in der Datenbank wiederzugeben
//Normalerweise wird die Aktualisierungsverarbeitung mit der Datenbank synchronisiert, wenn die Transaktion festgeschrieben wird.
//Explizite Synchronisierung, da im Testprozess keine Festschreibung erfolgt
@AfterEach
void tearDown() {
userDao.getEm().flush();
}
/**
*Überprüfen Sie, ob beim Erstellen ein neuer Datensatz erstellt wird
*Stellen Sie sicher, dass die Datenbank wie von der Entität erwartet neu geschrieben wurde, indem Sie sie mit der erwarteten Datenbank vergleichen
*/
@Test
@DatabaseSetup(value = "/CRUD/setUp/forCreate")
@ExpectedDatabase(value = "/CRUD/create/", assertionMode=DatabaseAssertionMode.NON_STRICT)
Mit der Methode void create wird ein neuer Benutzer erstellt() {
User user = new User();
user.setUserName("test3");
userDao.saveOrUpdate(user);
}
/**
*Überprüfen Sie, ob der vorhandene Datensatz durch den Aktualisierungsprozess aktualisiert wird
*Stellen Sie sicher, dass die Datenbank wie von der Entität erwartet neu geschrieben wurde, indem Sie sie mit der erwarteten Datenbank vergleichen
*/
@Test
@DatabaseSetup(value = "/CRUD/setUp/")
@ExpectedDatabase(value = "/CRUD/update/", assertionMode=DatabaseAssertionMode.NON_STRICT)
Benutzer 1 kann mit der Void-Update-Methode neu geschrieben werden() {
User user = new User();
user.setUserId(1L);
user.setUserName("test1mod");
userDao.saveOrUpdate(user);
}
/**
*Überprüfen Sie, ob der Datensatz durch den Löschvorgang gelöscht wird
*Bereiten Sie vor und nach der Verarbeitung eine Datenbank vor und überprüfen Sie die Gültigkeit, indem Sie vergleichen, ob das erwartete Ergebnis nach dem Löschen erzielt wird.
*/
@Test
@DatabaseSetup(value = "/CRUD/setUp/")
@ExpectedDatabase(value = "/CRUD/delete/", assertionMode=DatabaseAssertionMode.NON_STRICT)
Benutzer 1 kann mit der Methode void delete gelöscht werden() {
userDao.delete(1);
}
Nachdem wir einige neue haben, werfen wir einen kurzen Blick auf jeden einzelnen. Es gibt auch einige Einschränkungen hinsichtlich des Testcodes für die CURD-Verarbeitung von Datenbankoperationen. Schauen wir uns also auch diese an.
AfterEach
Dies ist eine Anmerkung für JUnit5 und beschreibt den Prozess, den Sie nach Ausführung jeder Testmethode einfügen möchten. Hier wird die Flush-Methode von EntityManager explizit aufgerufen. Die Flush-Methode erledigt die Arbeit, um die Entitäten im Persistenzkontext mit den Datensätzen in der Datenbank zu synchronisieren. Normalerweise wird diese Methode automatisch aufgerufen, wenn die Transaktion festgeschrieben wird, ohne dass sie sich dessen bewusst ist. Referenz
In diesem Testcode müssen Sie jedoch "RollBack" ausführen, um die Datenbank nach Abschluss des Datenbankvorgangs wiederherzustellen. Dann wird die Flush-Methode nicht aufgerufen, sodass das erwartete Ergebnis der Testmethode nicht in der Datenbank angezeigt wird und der Test nicht bestanden wird. Es gibt verschiedene Möglichkeiten, damit umzugehen, aber es scheint besser, die Flush-Methode explizit aufzurufen, wenn jede Methode eine Transaktion festschreibt, dh wenn der Prozess abgeschlossen ist, genau wie beim Ausführen einer App.
Durch Aufrufen der Flush-Methode nach Ausführung jeder Testmethode kann das erwartete Ergebnis korrekt überprüft werden.
ExpectedDatabase
Es wird gleichzeitig mit der Annotation DatabaseSetup verwendet und dient, wie der Name schon sagt, zur Überprüfung des Status der Datenbank nach Ausführung der Testmethode. Geben Sie wie bei der DatabaseSetup-Annotation das Verzeichnis an, in dem die CSV-Datei, die den Tabellenstatus des erwarteten Ergebnisses beschreibt, im Wert value gespeichert ist. Außerdem wird die Eigenschaft "assertionMode" festgelegt. Wenn Sie hier jedoch "NON_STRICT" festlegen, werden nur die in der CSV-Datei angegebenen Spalten überprüft, nicht alle Spalten.
In dieser Testklasse wird die "Transaktionsanmerkung" auf Klassenebene hinzugefügt. Das Festlegen dieser Annotation auf Klassenebene entspricht dem Hinzufügen einer Annotation zu allen Methoden in der Klasse. Einige Controller-Tests erfordern keine Transaktionssteuerung. Wenn Sie jedoch versuchen, jede Methode jedes Mal festzulegen, treten Auslassungen auf. Daher ist es besser, sie alle auf Klassenebene festzulegen.
Jetzt haben Sie ein Verständnis für die Verarbeitung, die für die CRUD-Verarbeitung erforderlich ist. Am Ende von Level 2 sind beim Testen von Datenbankoperationen einige Dinge zu beachten (weil ich süchtig danach bin). Ich hoffe es wird für Sie hilfreich sein.
Der obige Prozess ist ein Prozess zum Umschreiben eines vorhandenen Datensatzes. Ich denke, dass es mehrere Datensätze verarbeiten kann, aber in den meisten Fällen zielen Webanwendungen auf einen Datensatz. Zu diesem Zeitpunkt sind Schlüsselinformationen erforderlich, um das Verarbeitungsziel zu klären.
Ich denke, es gibt verschiedene Möglichkeiten, dies zu tun, aber ich denke, es ist einfach und leicht zu schreiben, "Geben Sie die ID im Datensatz der CSV-Datei an". Der hier zu beachtende Punkt ist das Konzept der Logik zur Durchführung des Tests. Da es etwas lang zu sein scheint, habe ich es in der Beilage geschrieben. Schauen Sie also bitte vorbei, wenn Sie interessiert sind.
Betrachten Sie den Fall der Registrierung eines neuen Datensatzes in der Datenbank mit einer Testmethode. Wenn die ID beispielsweise automatisch nummeriert und dem Setup-Datensatz zugewiesen wird, kann es zu einer Schlüsselverdoppelung kommen.
Wenn Sie in diesem Fall die ID festlegen, dass der Datensatz der Ergebnismenge ebenfalls nummeriert ist, ist es sicherer, den automatisch nummerierten Wert nicht zu steuern.
Wenn Sie als Lösung die Generierung eines neuen Datensatzes überprüfen möchten, ist es meiner Meinung nach besser, mit der Richtlinie fortzufahren, eine CSV-Datei ohne die ID zu verwenden und nur die Inhaltsspalte ohne die betreffende ID zu überprüfen.
In Level 2 sind viele neue Dinge herausgekommen. Wenn Sie sich jedoch ein wenig daran gewöhnt haben, können Sie klar schreiben und vor allem den Server wie einen manuellen Test starten, auf die Seite zugreifen, sie tatsächlich verarbeiten und die Datenbank aufrufen ... Ich denke, dass es ein sehr nützlicher Teil ist, weil es überprüft werden kann, ohne es zu tun. Selbst wenn Sie den Bereich bis Stufe 2 beherrschen, wird die Effizienz der Fehlerkorrektur während der Entwicklung erheblich verbessert.
Wenn Sie Level 2 abgeschlossen haben, sollten Sie in der Lage sein:
Spring Boot ist übrigens ein Framework zum Erstellen von Webanwendungen. Daher werden häufig POST-Anforderungen verwendet, wenn die Datenbank tatsächlich betrieben wird. Daher möchten wir auf Ebene 3 einen Blick auf die Validierung von POST-Anforderungen werfen. Das Level steigt, aber es ist etwas, das Sie mit Ihrem Wissen bisher vollständig verstehen können. Ich würde mich freuen, wenn Sie mir bis zum Ende folgen könnten (╹◡╹)
Schauen wir uns als nächstes den Testcode zur Validierung von POST-Anforderungen an. Da es lange dauern würde, den gesamten zu überprüfenden Code zu platzieren, möchte ich mich auf den Testcode konzentrieren, indem ich hier nur den Umriss der Anwendung beschreibe.
Auf Stufe 3 werden wir die TODO-Liste als Thema verwenden. Es ist einfach und kann die folgende einfache CRUD-Verarbeitung ausführen.
Es wird einige neue Kenntnisse über POST-Anfragen geben, aber es ist verständlich, wenn Sie über die bisherigen Kenntnisse verfügen. Schauen Sie sich daher nach einer umfassenden Überprüfung dieses Artikels den Testcode an. Der tatsächliche Testcode wird unten angezeigt. Es ist etwas länger, aber das meiste ist verständlich ... Ich bin glücklich.
TodoControllerTest.java
@DbUnitConfiguration(dataSetLoader = CsvDataSetLoader.class)
@TestExecutionListeners({
DependencyInjectionTestExecutionListener.class,
TransactionalTestExecutionListener.class,
DbUnitTestExecutionListener.class
})
@AutoConfigureMockMvc
@SpringBootTest(classes = {DbMvcTestApplication.class})
@Transactional
public class TodoControllerTest {
//mockMvc Mock-Objekt zur Verarbeitung von HTTP-Anforderungen und -Antworten ohne Bereitstellung auf dem Tomcat-Server
@Autowired
private MockMvc mockMvc;
@Autowired
private TodoDao todoDao;
@AfterEach
void tearDown() {
todoDao.getEm().flush();
}
/**
*Stellen Sie sicher, dass die Ansicht korrekt zurückgegeben wird
* @throws Exception
*/
@Test
Todo wird als Ansicht in der ungültigen Init-Verarbeitung übergeben() throws Exception {
this.mockMvc.perform(get("/todo/init"))
.andExpect(status().isOk())
.andExpect(view().name("todo"));
}
/**
*Stellen Sie sicher, dass der von DB erfasste Datensatz im Modell festgelegt ist
*Diesmal ist dies kein komplizierter Prozess. Wenn also ein Datensatz in der Datenbank an das Modell übergeben wird, wird davon ausgegangen, dass er normal funktioniert.
*
* @throws Exception
*/
@Test
@DatabaseSetup(value = "/TODO/setUp/")
Der void init-Prozess übergibt eine vorhandene Aufgabe an das Modell() throws Exception {
//Mit mockMvc/todo/Senden Sie eine Get-Anfrage an "init"
this.mockMvc.perform(get("/todo/init"))
//DB-Datensätze werden als Liste an das Modell übergeben
.andExpect(model().attribute("todoForm", hasProperty(
"todoList", hasItem(
hasProperty(
"task", is("task1")
)
)
)));
}
/**
*Überprüfen Sie anhand der Eingabe auf dem Bildschirm, ob neue Datensätze in der Datenbank registriert sind
* @throws Exception
*/
@Test
@DatabaseSetup(value = "/TODO/setUp/create")
@ExpectedDatabase(value = "/TODO/create/", assertionMode=DatabaseAssertionMode.NON_STRICT)
Eine neue Aufgabe wird durch ungültige Speicherverarbeitung in der Datenbank registriert() throws Exception {
this.mockMvc.perform(post("/todo/save")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
.param("newTask", "newTask"));
}
/**
*Überprüfen Sie, ob vorhandene Datensätze durch Bildschirmeingabe aktualisiert werden
*Da diesmal keine Bildschirminformationen verwendet werden, ist es nicht möglich, die automatisch nummerierte ID zu erhalten.
*Geben Sie daher dieses Mal das Aktualisierungsziel manuell an.
*Grundsätzlich ist die Reihenfolge der Liste nicht garantiert, daher scheint es notwendig zu sein, sie zum Zeitpunkt von SELECT zu sortieren.
* @throws Exception
*/
@Test
@DatabaseSetup(value = "/TODO/setUp/")
@ExpectedDatabase(value = "/TODO/update/", assertionMode=DatabaseAssertionMode.NON_STRICT)
Der ungültige Aktualisierungsprozess aktualisiert die vorhandene Aufgabe() throws Exception{
//"To do" mit mockMvc/Senden Sie eine Post-Anfrage an "Update"
long updateTargetId = 3L;
int updateTargetIndex = 2;
this.mockMvc.perform(post("/todo/update/" + updateTargetIndex + "/" + updateTargetId)
.param("todoList[" + updateTargetIndex + "].task", "task3mod")
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
);
}
/**
*Überprüfen Sie, ob die auf dem Bildschirm ausgewählte Aufgabe gelöscht wurde
* @throws Exception
*/
@Test
@DatabaseSetup(value = "/TODO/setUp/")
@ExpectedDatabase(value = "/TODO/delete/", assertionMode=DatabaseAssertionMode.NON_STRICT)
Der Vorgang zum Löschen der Leere löscht die vorhandene Aufgabe() throws Exception {
long deleteTargetId = 3L;
this.mockMvc.perform(post("/todo/delete/" + deleteTargetId)
.contentType(MediaType.APPLICATION_FORM_URLENCODED)
);
}
}
Im Folgenden werden die neu eingeführten Methoden und Punkte für POST-Anforderungen beschrieben, die bei der Bearbeitung von POST-Anforderungen zu beachten sind. Ich denke nicht, dass es beängstigend ist, weil Sie nur Parameter einstellen und Anfragen stellen.
Ein bemerkenswerter Punkt im POST-Anforderungstestcode ist das Festlegen der an die Anforderung übergebenen Parameter. Wenn Sie die POST-Anforderung jedoch bis zu einem gewissen Grad verstehen, können Sie sie intuitiv festlegen. Für POST-Anfragen empfehlen wir dies, da es eindeutig in MDN geschrieben ist.
Übrigens wird im Testcode mit "MockMvc" der Teil, in dem die GET-Anforderung von der perform-Methode auf Ebene 1 gestellt wurde, in die POST-Anforderung geändert. Danach können Sie mit der param-Methode Parameter im Schlüsselwertformat übergeben, sodass Sie einfach eine Anfrage gemäß dem in der tatsächlichen Anfrage übergebenen Formular stellen können. Sie können die param-Methode auch in einer GET-Anforderung aufrufen. In diesem Fall wird sie jedoch als Abfrageparameter gesendet. Hier wird die POST-Anforderung basierend auf dem Formular gesendet, sodass die Parameter im Anforderungshauptteil gespeichert werden.
Obwohl der contentType ohne Angabe funktioniert, ist es meiner Meinung nach besser, ihn so einzustellen, dass er der tatsächlichen POST-Anforderung so nahe wie möglich kommt.
Dieses Mal überprüfen wir hauptsächlich, ob die Datenbank durch die POST-Anforderung korrekt aktualisiert wurde. Zu diesem Zeitpunkt besteht das Problem darin, was von der POST-Anforderung verarbeitet wird. Ich möchte einen kurzen Blick auf jeden CRUD-Prozess werfen.
Wenn Sie einen neuen Datensatz erstellen, sind die Parameter für den neuen Datensatz unabhängig von der Tabelle, sodass Sie sich darüber keine Gedanken machen sollten.
Diesmal ist der Überprüfungsbereich so lange, bis der Ansichtsname an View übergeben wird. Daher ist es in Ordnung, wenn Sie das an Model übergebene Objekt überprüfen können. Auch hier gibt es also keinen Grund zur Sorge ...
Da die zu löschende ID durch den Pfad der Anforderung bestimmt wird, muss das Löschziel beim Erstellen einer Anforderung mit MockMvc eindeutig angegeben werden. Diese Konventionen sollten in der "Implementierung" vermieden werden, aber ich denke, Sie müssen sich im Testcode nicht zu viele Sorgen machen. Erstens definiert der Testcode den Status der Datenbank als "fest". Anstatt Änderungen und Erweiterungen anzunehmen, sollten Sie sich auf den Teil konzentrieren, "ob eine konstante Ausgabe immer von einer konstanten Eingabe erhalten wird". Die Stärke des Testcodes besteht darin, dass Sie immer das gleiche Ergebnis erzielen können, egal wie oft Sie es ausführen. Ich persönlich denke daher, dass Sie darüber nachdenken sollten, wie Sie es getrennt von der Implementierung schreiben können.
Gleiches gilt für den Update-Prozess. Wenn Sie jedoch beim Aktualisieren einen Datensatz in der Liste der Entitäten als Ziel festlegen möchten, müssen Sie einige Maßnahmen ergreifen, z. B. das Bearbeitungsziel von der Liste trennen und in einer separaten Aktualisierungsentität speichern. Bei diesem Aktualisierungsprozess ist die ID der Indexentität der Liste festgelegt, aber die Reihenfolge der Liste ist grundsätzlich nicht garantiert, sodass in der Anwendung auf Unternehmensebene in der obigen Form "immer" Es ist erforderlich, einen Zustand zu erstellen, in dem dasselbe Ergebnis mit derselben Eingabe erzielt werden kann. Ich möchte darüber schreiben, wenn ich mich an den Bildschirmtestcode (Wunsch) gewöhnt habe.
Obwohl es als Level 3 eingestuft wurde, war das meiste bisher in Form einer umfassenden Überprüfung, also verstehe ich ... ich verstehe ... !! Ich würde mich sehr freuen, wenn Sie könnten (:) Wenn Sie den Testcode der Stufe 3 verstehen, können Sie:
Es war länger als ich erwartet hatte, aber jetzt kann ich sehen, wie man einen einfachen Testcode für die CRUD-Verarbeitung schreibt. Wenn Sie den Implementierungsteil schreiben, müssen Sie sich der Dinge bewusst sein, die Sie normalerweise nicht kennen, und ich denke, es gab einige schwierige Teile. Das Schreiben von Testcode vertieft jedoch Ihr Verständnis des Frameworks und der Sprache, rationalisiert die Entwicklung und bietet viele Vorteile.
Und das Beste ist, dass Sie die Freude am Bestehen eines Tests nicht allein mit der Implementierung genießen können. Wenn Sie die erste Barriere überwunden haben, macht das Schreiben von Testcode viel Spaß.
Durch diesen Artikel kann ich möglicherweise Testcode mit Spring Boot schreiben ...? Ich würde es begrüßen, wenn Sie denken könnten. Ich bin noch nicht ausgereift in Bezug auf den Testcode, daher würde ich gerne weitere Erklärungen zum Testcode sehen.
Recommended Posts