Java JUnit Annotationen
Wichtige JUnit-5-Annotationen — @Test, @BeforeEach, @AfterEach, @BeforeAll, @AfterAll, @Disabled.
JUnit 5 ist das De-facto-Standard-Testframework für Java, und fast alles, was Sie ihm mitteilen, geschieht über Annotationen. Sie schreiben keine main-Methode und rufen keine Methoden selbst auf; Sie versehen gewöhnliche Methoden mit Annotationen wie @Test, @BeforeEach und @AfterAll, und die JUnit-Engine entdeckt sie per Reflection und führt sie in der richtigen Reihenfolge aus. Dieses Kapitel behandelt die wichtigsten Lebenszyklus-Annotationen — was jede bedeutet, wann sie ausgelöst wird und wie sie zusammenwirken, um jedem Test eine saubere, isolierte Testumgebung zu geben.
Wenn Sie neu im Framework sind, beginnen Sie mit der JUnit-Einführung; für die Assertion-Hilfsmethoden, die diese Tests aufrufen, siehe JUnit-Assertions. Annotationen selbst sind ein allgemeines Java-Feature, das in Java-Annotationen behandelt wird.
Die Annotationen befinden sich in org.junit.jupiter.api
Die JUnit-5-API ist das Jupiter-Modul. Die Annotationen, die Sie täglich verwenden, kommen alle aus einem einzigen Paket:
| Annotation | Gilt für | Wird ausgeführt |
|---|---|---|
@Test | eine Methode | einmal pro Testmethode |
@BeforeEach | eine Methode | vor jedem @Test |
@AfterEach | eine Methode | nach jedem @Test |
@BeforeAll | eine static-Methode | einmal, vor allen Tests in der Klasse |
@AfterAll | eine static-Methode | einmal, nach allen Tests in der Klasse |
@Disabled | eine Methode oder Klasse | nie (sie wird übersprungen und gemeldet) |
@DisplayName | eine Methode oder Klasse | setzt einen menschenlesbaren Namen in Berichten |
Eine mit @Test markierte Methode benötigt in JUnit 5 keinen public-Modifier (package-private ist in Ordnung) und muss void zurückgeben.
@Test: die Arbeitseinheit
Eine Testmethode überprüft etwas mit den statischen Hilfsmethoden in org.junit.jupiter.api.Assertions. Wenn eine Assertion fehlschlägt, wirft sie eine Ausnahme, und die Engine verzeichnet diesen einen Test als fehlgeschlagen, ohne die anderen zu stoppen.
import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
class CalculatorTest {
@Test
void addsTwoNumbers() {
Calculator calc = new Calculator();
assertEquals(5, calc.add(2, 3));
}
@Test
void throwsOnDivideByZero() {
Calculator calc = new Calculator();
assertThrows(ArithmeticException.class, () -> calc.divide(1, 0));
}
}Jeder @Test wird auf einer frischen Instanz der Testklasse ausgeführt — JUnit erstellt standardmäßig ein neues Objekt pro Test, sodass Felder, die in einem Test gesetzt werden, nicht in einen anderen übertragen werden können.
@BeforeEach und @AfterEach: Tests pro Testfall einrichten
Setup, das jeder Test benötigt, gehört in eine @BeforeEach-Methode; Teardown gehört in @AfterEach. Sie rahmen jeden @Test ein und geben jedem Test einen identischen Ausgangspunkt.
import org.junit.jupiter.api.*;
class OrderServiceTest {
private OrderService service;
@BeforeEach
void setUp() {
service = new OrderService(new InMemoryRepo()); // fresh state per test
}
@AfterEach
void tearDown() {
service.close(); // runs even if the test threw
}
@Test
void placesOrder() {
assertTrue(service.place("SKU-1", 2));
}
}@AfterEach wird auch dann ausgeführt, wenn der Test fehlschlägt, was es zum richtigen Ort macht, um Ressourcen freizugeben, die Sie in @BeforeEach geöffnet haben.
@BeforeAll und @AfterAll: einmal pro Klasse
Wenn das Setup teuer und gemeinsam nutzbar ist — ein Datenbank-Container, ein gestarteter eingebetteter Server — verwenden Sie @BeforeAll, um es einmal durchzuführen, und @AfterAll, um es einmal abzubauen. Da sie ausgeführt werden, bevor eine Instanz existiert, müssen sie static sein.
import org.junit.jupiter.api.*;
class RepositoryTest {
static Database db;
@BeforeAll
static void startDatabase() {
db = Database.start(); // runs once, before everything
}
@AfterAll
static void stopDatabase() {
db.stop(); // runs once, after everything
}
@Test
void savesRow() {
assertEquals(1, db.insert("hello"));
}
}Die vollständige Lebenszyklusreihenfolge für eine Klasse mit zwei Tests lautet: @BeforeAll → (@BeforeEach → @Test → @AfterEach) → (@BeforeEach → @Test → @AfterEach) → @AfterAll. Das Kapitel JUnit-Lebenszyklus geht auf diese Reihenfolge ausführlicher ein, einschließlich der Interaktion mit der Instanzerstellung.
@Disabled: überspringen ohne zu löschen
@Disabled schaltet einen Test (oder eine ganze Klasse) aus. Die Engine meldet ihn als übersprungen statt als bestanden oder fehlgeschlagen, sodass er sichtbar bleibt. Geben Sie immer einen Grund an.
@Test
@Disabled("flaky until the rate-limiter fix lands — see JIRA-1234")
void callsExternalApi() {
// not executed
}Ein ausgearbeitetes Beispiel: eine Mini-Test-Engine
Auf diesem Runner ist kein JUnit-JAR vorhanden, daher erstellt das folgende Programm eine winzige eigene Engine mit genau derselben Form wie JUnit. Es deklariert Marker-Annotationen (@BeforeAll, @BeforeEach, @Test, @AfterEach, @AfterAll, @Disabled), definiert eine kleine annotierte Testklasse und verwendet dann Reflection — genau das, was JUnit's Engine intern macht — um die Methoden in der Lebenszyklusreihenfolge zu entdecken und auszuführen und eine Bestanden/Fehlgeschlagen/Übersprungen-Zusammenfassung auszugeben.
Was man aus dem Durchlauf mitnehmen kann:
@BeforeAllwurde genau einmal ganz oben ausgegeben und@AfterAllgenau einmal ganz unten — das Setup und Teardown auf Klassenebene umrahmt den gesamten Durchlauf, weshalb JUnit verlangt, dass siestaticsind.- Jedem ausgeführten
@Testgeht eine@BeforeEach-Zeile voraus und folgt eine@AfterEach-Zeile, sodass jeder Test gegen eine frisch vorbereitete Testumgebung lief und nach sich aufgeräumt hat — die testweise Einrahmung, die Tests voneinander unabhängig hält. - Die
flaky-Methode trug@Disabled, also wurde(skipped via @Disabled)ausgegeben und ihr Körper wurde nie ausgeführt; die darin enthaltene fehlschlagende Assertion wurde nie erreicht — das ist der eigentliche Sinn des Deaktivierens statt Löschens. - Die Entdeckungsschleife wirkt nur auf Methoden, bei denen
isAnnotationPresent(Test.class)wahr ist — Annotationen sind nur Metadaten, und es ist die Engine, die sie über Reflection liest und in Verhalten verwandelt, genau so wie echtes JUnit funktioniert. - Die letzte Zeile meldet
2 passed, 0 failed, 1 skipped: zwei echte Tests bestanden, keiner fehlgeschlagen, und der deaktivierte wurde als übersprungen gezählt, statt still ignoriert zu werden — dieselbe Bestanden/Fehlgeschlagen/Übersprungen-Abrechnung, die ein JUnit-Bericht liefert.