CodeGym /Java-Blog /Random-DE /Unit-Tests in Java mit JUnit
Autor
Edward Izraitel
Software Engineer at Champions Oncology

Unit-Tests in Java mit JUnit

Veröffentlicht in der Gruppe Random-DE

Was ist Unit-Test in Java?

Bevor wir uns mit dem Erlernen von JUnit in Java befassen, werfen wir einen kurzen Überblick darüber, was Unit-Tests sind und warum sie so beliebt sind (wenn Sie sich bereits damit auskennen, fahren Sie mit „Wie schreibe ich einen JUnit-Test in Java?“ fort). Unit-Tests in Java machen die Softwareentwicklung im großen Maßstab wesentlich effizienter und müheloser. Es kann sowohl Einzelpersonen als auch Teams dabei helfen, unzählige Stunden beim Debuggen einzusparen und den Zusammenarbeitsprozess enorm zu rationalisieren. Unit-Tests in Java mit JUnit - 1Die Grundidee von Unit-Tests ist folgende: Schreiben Sie atomare Tests einzelner Features (sogenannte Unit-Tests) und fügen Sie nach dem Testen langsam weitere Features hinzu und stellen Sie sicher, dass die vorherigen funktionieren. Es ist eine äußerst einfache, aber wirkungsvolle Idee. Als Beispiel dafür, wie dieser Prozess aussehen könnte, stellen Sie sich vor, Sie würden einen virtuellen wissenschaftlichen Taschenrechner bauen. Zusätzlich zu den offensichtlichen arithmetischen Operatoren ( +, -, x, %) verfügt dieser Rechner über erweiterte Funktionen, für deren Funktion weitere Unterfunktionen erforderlich sind. Um Exponenten zu berechnen, muss Ihr Rechner in der Lage sein, korrekt zu multiplizieren. Ein Unit-Test-Ansatz zum Erstellen und Testen dieses Rechners wäre also:
  • Schreiben Sie eine Additionsfunktion. Testen Sie es sorgfältig, ändern Sie es und wiederholen Sie es, bis es funktioniert.
  • Machen Sie dasselbe für Subtraktions-, Multiplikations- und Divisionsfunktionen.
  • Verwenden Sie diese Basisoperatoren, um erweiterte Operatorfunktionen wie Exponenten zu schreiben, und testen Sie diese Funktionen anschließend ebenfalls.
Dadurch wird sichergestellt, dass Funktionen, die auf anderen kleineren Unterfunktionen aufbauen, nicht nur eigenständig ordnungsgemäß funktionieren, sondern auch keine fehlerhaften Unterfunktionen enthalten. Wenn ich beispielsweise die Exponentenfunktion teste und etwas schief geht, weiß ich, dass der Fehler wahrscheinlich nicht in der Multiplikations-Unterfunktion liegt, da die Multiplikationsfunktion bereits ausführlich getestet wurde. Dadurch entfällt die gesamte Codemenge, die ich zurückverfolgen und untersuchen muss, um den Fehler zu finden, erheblich. Hoffentlich macht dieses triviale Beispiel deutlich, wie der Denkprozess rund um Unit Testing strukturiert ist. Aber wie interagieren Unit-Tests mit dem Rest des Softwareentwicklungsprozesses? Was ist, wenn Sie über noch komplexere Funktionen verfügen, die zusammenarbeiten und kommunizieren müssen? Unit-Tests reichen nicht aus, um sicherzustellen, dass solch komplexe Funktionen ordnungsgemäß zusammenarbeiten können. Tatsächlich ist es nur der erste Schritt der vier Stufen des Softwaretests (ich verwende Großbuchstaben, weil ich mich auf den Industriestandard oder den gängigsten Ansatz zum Testen von Software beziehe). Die letzten drei Schritte sindIntegrationstests , Systemtests und Akzeptanztests. Diese bedeuten wahrscheinlich alle genau das, was Sie denken, aber lassen Sie mich klarstellen: Integrationstests sind das, was wir tun würden, um sicherzustellen, dass die oben erwähnten „komplexen Funktionen“ ordnungsgemäß miteinander interagieren. (z. B. sicherstellen, dass der Rechner „3 + 7 * 4 – 2“ verarbeiten kann). Beim Systemtest wird der Gesamtentwurf eines bestimmten Systems getestet. Da in einem Produkt häufig mehrere Systeme mit komplexen Funktionen zusammenarbeiten, gruppieren Sie diese in Systemen und testen sie einzeln. (Wenn Sie beispielsweise einen Grafikrechner bauen würden, würden Sie zuerst das arithmetische „System“ für den Umgang mit Zahlen erstellen und testen, bis es wie beabsichtigt funktioniert, und dann würden Sie das grafische „System“ für den Umgang mit Abhebungen erstellen und testen es würde auf dem arithmetischen System aufbauen). Akzeptanztests sind Tests auf Benutzerebene. Es geht darum, zu prüfen, ob alle Systeme synchron arbeiten können, um ein fertiges Produkt zu erstellen, das von Benutzern akzeptiert werden kann (z. B. Benutzer, die den Taschenrechner testen). Softwareentwickler können diesen letzten Schritt des Prozesses manchmal ignorieren, da Unternehmen Benutzertests (Betatests) häufig separat von anderen Mitarbeitern durchführen lassen.

Wie schreibe ich einen JUnit-Test in Java?

Nachdem Sie nun eine klarere Vorstellung von den Vorteilen und Einschränkungen von Unit-Tests haben, werfen wir einen Blick auf den Code! Wir werden ein beliebtes Java-Testframework namens JUnit verwenden (ein weiteres beliebtes ist TestNG, das Sie bei Bedarf auch verwenden können. Sie sind syntaktisch sehr ähnlich; TestNG ist von JUnit inspiriert). Sie können JUnit hier herunterladen und installieren. Für diesen Beispielcode werden wir mit dem Beispiel des „wissenschaftlichen Taschenrechners“ fortfahren, das ich zuvor erwähnt habe. Es ist ziemlich einfach, den Kopf herumzureißen, und der Testcode ist super einfach. Die übliche Praxis besteht darin, für jede Ihrer Klassen separate Testklassen zu schreiben, also machen wir das auch. Nehmen wir an, dass wir zu diesem Zeitpunkt eine Math.javaDatei mit allen darin enthaltenen mathematischen Funktionen (einschließlich Math.add) haben und eine schreibenMathTests.javaDatei im selben Paket. Nun richten wir Importanweisungen und Klassenkörper ein: (MÖGLICHE JUnit-INTERVIEWFRAGE: Möglicherweise werden Sie gefragt, wo Sie Ihren JUnit-Test platzieren und ob Sie Ihre Quelldateien importieren müssen oder nicht. Wenn Sie Ihre Testklassen im selben Paket schreiben wie Ihre Hauptklassen, dann benötigen Sie keine Importanweisungen für Ihre Quelldateien in der Testklasse. Andernfalls stellen Sie sicher, dass Sie Ihre Quelldateien importieren!)

import org.junit.jupiter.Test;    //gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; //less typing :) 

public class MathTests {
	//...
}
Die erste Importanweisung gibt uns den @TestHeader. Wir schreiben „ @Test“ direkt über jede Testfunktionsdefinition, damit JUnit weiß, dass es sich um einen einzelnen Unit-Test handelt, der separat ausgeführt werden kann. Später werde ich Ihnen zeigen, wie Sie mit diesem Header bestimmte Unit-Tests ausführen können. Die zweite Importanweisung erspart uns ein wenig Tipparbeit. Die primäre JUnit-Funktion, die wir zum Testen unserer Funktionen verwenden, ist to Assert.assertEquals(), die zwei Parameter (tatsächlicher Wert und erwarteter Wert) annimmt und sicherstellt, dass sie gleich sind. Mit dieser zweiten Importanweisung können wir einfach „ “ eingeben, assertEquals(...anstatt jedes Mal angeben zu müssen, zu welchem ​​Paket es gehört. Schreiben wir nun einen sehr einfachen Testfall, um zu überprüfen, ob 2 + 2 tatsächlich 4 ist!

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test
	public void add_twoPlusTwo_returnsFour(){
	final int expected = 4;
	final int actual = Math.add(2, 2);
	assertEquals(“2+2 is 4”, actual, expected);
	}
}
Sehen wir uns jede der fünf Zeilen der Testfunktion und ihre Funktion an: Zeile 5: Dieser @TestHeader gibt an, dass es sich bei der folgenden Funktionsdefinition add_twoPlusTwo_returnsFour()tatsächlich um eine Testfunktion handelt, die JUnit separat ausführen kann. Zeile 6: Dies ist die Funktionssignatur für unseren Testfall. Testfälle sind immer sehr einzigartig; Sie testen nur ein bestimmtes Beispiel, z. B. 2+2=4. Es ist üblich, Ihre Testfälle in der Form „ [function]_[params]_returns[expected]()“ zu benennen. Dabei handelt es sich [function]um den Namen der Funktion, die Sie testen, [params]um die spezifischen Beispielparameter, die Sie testen, und [expected]um den erwarteten Rückgabewert der Funktion. Testfunktionen haben fast immer den Rückgabetyp „ void“, da der Hauptzweck der gesamten Funktion darin besteht, ausgeführt zu werdenassertEquals, das an die Konsole ausgibt, ob Ihr Test bestanden wurde oder nicht; Sie benötigen keine weiteren Daten, die irgendwo zurückgegeben werden müssen. Zeile 7: Wir deklarieren eine „ final“-Variable vom Rückgabetyp Math.add (int)und nennen sie gemäß der Konvention „erwartet“. Sein Wert ist die Antwort, die wir erwarten (4). Zeile 8: Wir deklarieren eine „ final“-Variable vom Rückgabetyp Math.add (int)und nennen sie gemäß der Konvention „actual“. Sein Wert ist das Ergebnis von Math.add(2, 2). Zeile 9: Die goldene Linie. Dies ist die Zeile, die tatsächliche und erwartete Ergebnisse vergleicht und uns nur dann mitteilt, dass wir den Test bestanden haben, wenn sie gleich sind. Der erste übergebene Parameter „2+2 ist 4“ ist eine Beschreibung der Testfunktion.

Was passiert, wenn meine Funktion eine Ausnahme auslösen sollte?

Wenn Ihr spezifisches Testbeispiel eine Ausnahme auslösen sollte, anstatt zu behaupten, dass ein tatsächlicher und ein erwarteter Wert gleich sind, dann verfügt JUnit über eine Möglichkeit, dies im @TestHeader zu verdeutlichen. Schauen wir uns unten ein Beispiel an. Angenommen, wir haben eine Math.javaaufgerufene Funktion Math.divide, möchten wir sicherstellen, dass Eingaben nicht durch 0 geteilt werden können. Stattdessen Math.divide(a, 0)sollte der Versuch, einen beliebigen „a“-Wert aufzurufen, eine Ausnahme auslösen ( ArithmeticException.class). Wir geben dies im Header als solches an:

import org.junit.jupiter.Test; // gives us the @Test header
import static org.junit.jupiter.api.Assertions.assertEquals; // less typing :) 


public class MathTests {
	@Test (expectedExceptions = ArithmeticException.class)
	public void divide_byZero_throwsException() throws ArithmeticException{
	Math.divide(1, 0);
	}
}
Sie können mehr als eine Ausnahme für haben expectedExceptions. Stellen Sie jedoch sicher, dass Sie Klammern und Kommas verwenden, um Ihre Ausnahmeklassen wie folgt aufzulisten:

expectedException = {FirstException.class, SecondException.class, … }

Wie führe ich meine JUnit-Tests in Java aus?

So fügen Sie JUnit zu IntelliJ hinzu: https://stackoverflow.com/questions/19330832/setting-up-junit-with-intellij-idea Sie können Ihr Projekt so ausführen, wie Sie normalerweise die Tests ausführen würden. Wenn Sie alle Tests in einer Testklasse ausführen, werden diese in alphabetischer Reihenfolge ausgeführt. In JUnit 5 können Sie den Tests eine Priorität hinzufügen, indem Sie ein @OrderTag hinzufügen. Ein Beispiel:

@TestMethodOrder(OrderAnnotation.class)
public class Tests {
…
@Test
@Order(2)
public void a_test() { … }

@Test
@Order (1)
public void b_test() { … }
…
}
Auch wenn es alphabetisch und im Code a_test()vorher steht , wird es hier vorher ausgeführt , da 1 vor 2 in der Reihenfolge steht. Das ist also alles für die Grundlagen von JUnit. Lassen Sie uns nun ein paar häufig gestellte Fragen im Vorstellungsgespräch zu JUnit beantworten und dabei noch mehr über JUnit erfahren! b_test()b_test()a_test()

Fragen zum JUnit-Interview (zusätzliche Informationen)

Hier habe ich die beliebtesten JUnit-Interviewfragen zusammengestellt. Wenn Sie etwas hinzufügen möchten, können Sie dies gerne in den Kommentaren unten tun. F: Welche Methode können Sie in Ihrer Testmethode aufrufen, um einen Test automatisch nicht bestehen zu lassen? A: fail(“Fehlerbeschreibung hier!”); F: Sie testen eine Hundeklasse. Um ein Dog-Objekt zu testen, müssen Sie es instanziieren, bevor Sie Tests darauf ausführen können. Sie schreiben also eine setUp()-Funktion, um den Hund zu instanziieren. Sie möchten diese Funktion während des gesamten Tests nur einmal ausführen. Was müssen Sie direkt über der Funktionssignatur setUp() einfügen, damit JUnit weiß, dass es setUp() ausführen muss, bevor die Tests ausgeführt werden? A: @BeforeClass (@BeforeAll in JUnit 5) F:Wie muss die Funktionssignatur der oben beschriebenen Funktion setUp() lauten? A: öffentliche statische Leere. Jede Funktion mit @BeforeClass (@BeforeAll in JUnit 5) oder @AfterClass (@AfterAll in JUnit 5) muss statisch sein. F: Sie sind mit dem Testen der Hundeklasse fertig. Sie schreiben die Funktion void tearDown(), die nach jedem Test Daten bereinigt und Informationen auf der Konsole ausgibt. Sie möchten, dass diese Funktion nach jedem einzelnen Test ausgeführt wird. Was müssen Sie direkt über der Funktionssignatur „tearDown()“ einfügen, damit JUnit nach jedem Test weiß, dass „tearDown()“ ausgeführt werden soll? A: @After (@AfterEach in JUnit 5)
Kommentare
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION