Seien Sie am 30. April dabei: Vorstellung von Parasoft C/C++test CT für kontinuierliche Tests und Compliance-Exzellenz | Registrierung

So erstellen Sie parametrisierte JUnit-Tests

Kopfschuss von Brian McGlauflin,
19. September 2018
7 min lesen

Das Schreiben von parametrisiertem Testcode kann eine Menge Arbeit sein, da für jeden Test eine Menge korrekt geschriebener Code erforderlich ist. Glücklicherweise erfahren Sie hier, wie Sie Parasoft Jtest verwenden können, um Ihre parametrisierten Tests automatisch zu generieren, ohne Boilerplate-Codes schreiben zu müssen.

Parametrisierte Tests sind eine gute Möglichkeit, mehrere Testfälle zu definieren und auszuführen, wobei der einzige Unterschied zwischen ihnen die Daten sind. Hier sehen wir uns drei verschiedene Frameworks an, die üblicherweise bei JUnit-Tests verwendet werden.

Beim Schreiben von Komponententests werden üblicherweise Methodeneingabeparameter und erwartete Ergebnisse in der Testmethode selbst initialisiert. In einigen Fällen reicht es aus, einen kleinen Satz von Eingaben zu verwenden. Es gibt jedoch Fälle, in denen wir eine große Anzahl von Werten verwenden müssen, um alle Funktionen in unserem Code zu überprüfen. Parametrisierte Tests sind eine gute Möglichkeit, mehrere Testfälle zu definieren und auszuführen, wobei der einzige Unterschied zwischen ihnen die Daten sind. Sie können das Codeverhalten für eine Vielzahl von Werten, einschließlich Grenzfällen, validieren. Parametrisierungstests können die Codeabdeckung erhöhen und die Sicherheit bieten, dass der Code wie erwartet funktioniert.

Es gibt eine Reihe guter Parametrisierungs-Frameworks für Java. In diesem Artikel werden drei verschiedene Frameworks betrachtet, die üblicherweise bei JUnit-Tests verwendet werden, mit einem Vergleich zwischen ihnen und Beispielen für die Struktur der Tests. Schließlich werden wir untersuchen, wie die Erstellung parametrisierter Tests vereinfacht und beschleunigt werden kann.

JUnit-parametrisierte Test-Frameworks

Vergleichen wir die drei gängigsten Frameworks: JUnit 3, JunitParams und JUnit 4. Jedes JUnit-Parametrisierungsframework hat seine eigenen Stärken und Schwächen.

Einheit 4

Vorteile:

  • Dies ist das in JUnit 4 integrierte Parametrisierungsframework, sodass keine zusätzlichen externen Abhängigkeiten erforderlich sind.
  • Es unterstützt ältere Versionen von Java (JDK 7 und älter).

Nachteile:

  • Testklassen verwenden Felder und Konstruktoren, um Parameter zu definieren, die Tests ausführlicher machen.
  • Für jede getestete Methode ist eine separate Testklasse erforderlich.

JunitParams

Vorteile:

  • Vereinfacht die Parametersyntax, indem Parameter direkt an eine Testmethode übergeben werden können.
  • Ermöglicht mehrere Testmethoden (jede mit ihren eigenen Daten) pro Testklasse.
  • Unterstützt CSV-Datenquellen sowie annotationsbasierte Werte (keine Methode erforderlich).

Nachteile:

  • Erfordert, dass das Projekt mit der JunitParams-Abhängigkeit konfiguriert wird.
  • Beim Ausführen und Debuggen von Tests müssen alle Tests innerhalb der Klasse ausgeführt werden. Es ist nicht möglich, eine einzelne Testmethode innerhalb einer Testklasse auszuführen.

Einheit 5

Vorteile:

  • Dieses Parametrisierungsframework ist in JUnit 5 integriert und verbessert das, was in JUnit 4 enthalten war.
  • Hat eine vereinfachte Parametersyntax wie JunitParams.
  • Unterstützt mehrere Datensatzquellentypen, einschließlich CSV und Annotation (keine Methode erforderlich).
  • Obwohl keine zusätzlichen Abhängigkeiten erforderlich sind, wird mehr als eine .jar-Datei benötigt.

Nachteile:

  • Benötigt Java 8 und eine neuere Version des Build-Systems (Gradle 4.6 oder Maven Surefire 2.21).
  • Wird in Ihrer IDE möglicherweise noch nicht unterstützt (derzeit unterstützen nur Eclipse und IntelliJ JUnit 5).

Beispiel

Angenommen, wir haben eine Methode, mit der Kreditanträge für eine Bank verarbeitet werden. Möglicherweise schreiben wir einen Komponententest, der den Betrag der Kreditanfrage, den Anzahlungsbetrag und andere Werte angibt. Wir würden dann Zusicherungen erstellen, die die Antwort bestätigen - das Darlehen kann genehmigt oder abgelehnt werden, und in der Antwort können die Bedingungen des Darlehens angegeben werden.

Beispielsweise:

public LoanResponse requestLoan (float creditAmount, float downPayment, float availableFunds) {LoanResponse response = new LoanResponse (); response.setApproved (true); if (availableFunds <downPayment) {response.setApproved (false); response.setMessage ("error.insufficient.funds.for.down.payment"); Antwort zurückgeben; } if (Anzahlung / DarlehenAmount <0.1) {response.setApproved (false); response.setMessage ("error.insufficient.down.payment"); } return response; }}

Roh anzeigen

parametrized_test_example_1.java

Veranstaltet von GitHub

Schauen wir uns zunächst einen regelmäßigen Test für die oben beschriebene Methode an:

@Test public void testRequestLoan () löst Throwable aus {// Given LoanProcessor underTest = new LoanProcessor (); // Wenn LoanResponse result = underTest.requestLoan (1000f, 200f, 250f); // Dann assertNotNull (Ergebnis); assertTrue (result.isApproved ()); assertNull (result.getMessage ()); }}

Roh anzeigen

parametrized_test_example_2.java

Veranstaltet von GitHub

In diesem Beispiel testen wir unsere Methode, indem wir ein Darlehen in Höhe von 1000 USD mit einer Anzahlung von 200 USD anfordern und angeben, dass dem Antragsteller 250 USD an verfügbaren Mitteln zur Verfügung stehen. Der Test bestätigt dann, dass das Darlehen genehmigt wurde und in der Antwort keine Nachricht enthalten ist.

Um sicherzustellen, dass unsere Darlehen anfordern() Methode wird gründlich getestet, wir müssen mit einer Vielzahl von Anzahlungen, angeforderten Darlehensbeträgen und verfügbaren Mitteln testen. Testen wir zum Beispiel eine Kreditanfrage in Höhe von 1 Million US-Dollar ohne Anzahlung, die abgelehnt werden sollte. Wir könnten den vorhandenen Test einfach mit unterschiedlichen Werten duplizieren, aber da die Testlogik dieselbe wäre, ist es effizienter, den Test stattdessen zu parametrisieren.

Wir werden den angeforderten Darlehensbetrag, die Anzahlung und die verfügbaren Mittel sowie die erwarteten Ergebnisse parametrisieren: ob das Darlehen genehmigt wurde und die Nachricht nach der Validierung zurückgegeben wird. Jeder Satz von Anforderungsdaten wird zusammen mit den erwarteten Ergebnissen zu einem eigenen Testfall.

Ein Beispiel für einen parametrisierten Test mit JUnit 4 Parametrisiert

Beginnen wir mit einem parametrisierten Junit 4-Beispiel. Um einen parametrisierten Test zu erstellen, müssen wir zuerst die Variablen für den Test definieren. Wir müssen auch einen Konstruktor einbinden, um sie zu initialisieren:

@RunWith (Parameterized.class) öffentliche Klasse LoanProcessorParameterizedTest {float creditAmount; float downPayment; float availableFunds; Boolescher ExpectApproved; String expectedMessage; public LoanProcessorParameterizedTest (float creditAmount, float downPayment, float availableFunds, boolescher ExpectApproved, String expectedMessage) {this.loanAmount = creditAmount; this.downPayment = downPayment; this.availableFunds = availableFunds; this.expectApproved = expectedApproved; this.expectedMessage = expectedMessage; } // ...}

Roh anzeigen

parametrized_test_example_3.java

Veranstaltet von GitHub

Hier sehen wir, dass der Test das verwendet @ RunWith Anmerkung, um anzugeben, dass der Test mit dem Junit4-parametrisierten Läufer ausgeführt wird. Dieser Läufer muss nach einer Methode suchen, die den Wertesatz für den Test bereitstellt (mit Anmerkungen versehen) @ Parameter), initialisieren Sie den Test ordnungsgemäß und führen Sie die Tests mit mehreren Zeilen aus.

Beachten Sie, dass jeder Parameter als Feld in der Testklasse definiert ist und der Konstruktor diese Werte initialisiert (Sie können Werte auch mit dem in die Felder einfügen @Parameter Anmerkung, wenn Sie keinen Konstruktor erstellen möchten). Für jede Zeile im Wertesatz wird die Parametriert Der Läufer instanziiert die Testklasse und führt jeden Test in der Klasse aus.

Fügen wir eine Methode hinzu, die die Parameter für die bereitstellt Parametriert Läufer:

@Parameters (name = "Führen Sie {index} aus: creditAmount = {0}, downPayment = {1}, availableFunds = {2}, expectedApproved = {3}, expectedMessage = {4}") public static Iterable data () löst Throwable aus {return Arrays.asList (neues Objekt [] [] {{1000.0f, 200.0f, 250.0f, true, null}}); }}

Roh anzeigen

parametrized_test_example_4.java

Veranstaltet von GitHub

Die Wertesätze werden als erstellt Liste of Betreff Arrays von der Daten() Methode, die mit kommentiert wird @ Parameter. Beachten Sie, dass @ Parameter Legt den Namen des Tests mithilfe von Platzhaltern fest, die beim Ausführen des Tests ersetzt werden. Dies macht es einfacher, Werte in Testergebnissen zu sehen, wie wir später sehen werden. Derzeit gibt es nur eine Datenzeile, in der ein Fall getestet wird, in dem das Darlehen genehmigt werden sollte. Wir können weitere Zeilen hinzufügen, um die Abdeckung der zu testenden Methode zu erhöhen.

@Parameters (name = "Führen Sie {index} aus: creditAmount = {0}, downPayment = {1}, availableFunds = {2}, expectedApproved = {3}, expectedMessage = {4}") public static Iterable data () löst Throwable aus {return Arrays.asList (neues Objekt [] [] {{1000.0f, 200.0f, 250.0f, true, null}, {1000.0f, 50.0f, 250.0f, false, "error.insufficient. down.payment "}, {1000.0f, 200.0f, 150.0f, false," error.insufficient.funds.for.down.payment "}}); }}

Roh anzeigen

parametrized_test_example_5.java

Veranstaltet von GitHub

Hier haben wir einen Testfall, in dem das Darlehen genehmigt werden würde, und zwei Fälle, in denen es aus unterschiedlichen Gründen nicht genehmigt werden sollte. Möglicherweise möchten wir Zeilen hinzufügen, in denen Null- oder negative Werte verwendet werden, sowie Randbedingungen testen.

Wir sind jetzt bereit, die Testmethode zu erstellen:

@Test public void testRequestLoan () löst Throwable aus {// Given LoanProcessor underTest = new LoanProcessor (); // Wenn LoanResponse result = underTest.requestLoan (creditAmount, downPayment, availableFunds); // Dann assertNotNull (Ergebnis); assertEquals (expectedApproved, result.isApproved ()); assertEquals (expectedMessage, result.getMessage ()); }}

Roh anzeigen

parametrized_test_example_6.java

Veranstaltet von GitHub

Hier verweisen wir beim Aufrufen von auf die Felder Darlehen anfordern() Methode und Validierung der Ergebnisse.

JunitParams Beispiel

Die JunitParams-Bibliothek vereinfacht die parametrisierte Testsyntax, indem Parameter direkt an die Testmethode übergeben werden können. Die Parameterwerte werden von einer separaten Methode bereitgestellt, auf deren Namen in der verwiesen wird @ Parameter Anmerkung.

@RunWith (JUnitParamsRunner.class) öffentliche Klasse LoanProcessorParameterizedTest2 {@Test @Parameters (method = "testRequestLoan_Parameters") public void testRequestLoan ("nicht verwendet") privates statisches Objekt [] [] testRequestLoan_Parameters () löst Throwable {// Parameter aus: creditAmount = {0}, downPayment = {1}, availableFunds = {2}, expectedApproved = {3}, expectedMessage = {4 } neues Objekt zurückgeben [] [] {{1000.0f, 200.0f, 250.0f, true, null}, {1000.0f, 50.0f, 250.0f, false, "error.insufficient.down.payment"}, {1000.0f , 200.0f, 150.0f, false, "error.insufficient.funds.for.down.payment"}}; }}

Roh anzeigen

parametrized_test_example_7.java

Veranstaltet von GitHub

JunitParams bietet den zusätzlichen Vorteil, dass die Verwendung von CSV-Dateien zur Bereitstellung von Werten zusätzlich zur Bereitstellung der Werte im Code unterstützt wird. Dadurch kann der Test von den zu aktualisierenden Daten und Datenwerten entkoppelt werden, ohne den Code zu aktualisieren.

Junit 5 Beispiel

JUnit 5 behebt einige der Einschränkungen und Mängel von JUnit 4. Wie JunitParams vereinfacht auch Junit 5 die Syntax parametrisierter Tests. Die wichtigsten Änderungen in der Syntax sind:

  • Die Testmethode ist mit kommentiert @ParametrierterTest statt @Prüfung
  • Die Testmethode akzeptiert Parameter direkt, anstatt Felder und einen Konstruktor zu verwenden
  • Das @ RunWith Annotation wird nicht mehr benötigt

Das Definieren des gleichen Beispiels in Junit 5 würde folgendermaßen aussehen:

öffentliche Klasse LoanProcessorParameterizedTest {@ParameterizedTest (name = "Run {index}: creditAmount = {0}, downPayment = {1}, availableFunds = {2}, expectedApproved = {3}, expectedMessage = {4}") @MethodSource (" testRequestLoan_Parameters ") public void testRequestLoan (Float-DarlehenAmount, Float-DownPayment, Float AvailableFunds, Boolean ExpectApproved, String ExpectedMessage) löst Throwable {...} static Stream aus testRequestLoan_Parameters () löst Throwable {return Stream.of (Argumente.of (1000.0f, 200.0f, 250.0f, true, null), Argumente.of (1000.0f, 50.0f, 250.0f, false, "error.insufficient.down) aus .payment "), Arguments.of (1000.0f, 200.0f, 150.0f, false," error.insufficient.funds.for.down.payment ")); }}

Roh anzeigen

parametrized_test_example_8.java

Veranstaltet von GitHub

Erstellen Sie effizient parametrisierte Tests

Wie man sich vorstellen kann, kann das Schreiben des oben parametrisierten Tests ein wenig Arbeit sein. Für jedes parametrisierte Test-Framework gibt es ein bisschen Boilerplate-Code, der korrekt geschrieben werden muss. Es kann schwierig sein, sich an die richtige Struktur zu erinnern, und das Schreiben dauert einige Zeit. Um dies viel einfacher zu machen, können Sie verwenden Parasoft Jtest automatisch parametrisierte Tests wie die oben beschriebenen zu generieren. Wählen Sie dazu einfach die Methode aus, für die Sie einen Test generieren möchten (in Eclipse oder IntelliJ):

Der Test wird unter Verwendung von Standardwerten und Zusicherungen generiert. Anschließend können Sie den Test mit realen Eingabewerten und Zusicherungen konfigurieren und der data () -Methode weitere Datenzeilen hinzufügen.

Ausführen des parametrisierten Tests

Parasoft Jtest kann parametrisierte Tests direkt in Eclipse und IntelliJ ausführen.

Die JUnit-Ansicht in Eclipse

Beachten Sie, dass der Name jedes Tests, wie gezeigt, Eingabewerte aus dem Datensatz und erwartete Ergebniswerte enthält. Dies kann das Debuggen des Tests erheblich vereinfachen, wenn er fehlschlägt, da die Eingabeparameter und erwarteten Ausgaben für jeden Fall angezeigt werden.

Sie können auch die Aktion Alle ausführen von Parasoft Jtest verwenden:

Die Flow Tree-Ansicht in Parasoft Jtest

Es analysiert den Testablauf und liefert detaillierte Informationen zum vorherigen Testlauf. Auf diese Weise können Sie sehen, was im Test passiert ist, ohne den Test mit Haltepunkten oder Debugging-Anweisungen erneut ausführen zu müssen. Beispielsweise können Sie parametrisierte Werte in der Variablenansicht anzeigen:

Die Variablenansicht in Parasoft Jtest

Zusammenfassung

Jedes der drei Frameworks, die wir überprüft haben, ist eine gute Wahl und funktioniert gut. Bei Verwendung von JUnit 4 bevorzuge ich JunitParams gegenüber dem integrierten parametrisierten Framework von JUnit 4, da die Testklassen sauberer gestaltet sind und mehrere Testmethoden in derselben Klasse definiert werden können. Wenn Sie jedoch JUnit 5 verwenden, würde ich das integrierte JUnit 5-Framework empfehlen, da es die Mängel in JUnit 4 behebt und keine zusätzlichen Bibliotheken erfordert. Benutze ich auch gerne Einheitentests von Parasoft Funktionen, um das Erstellen, Ausführen und Debuggen parametrisierter Tests effizienter zu gestalten.

Erfahren Sie, wie Sie mit Parasoft Jtest Ihre Java-Codequalität und Teamproduktivität verbessern können.