W3docs

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:

AnnotationGilt fürWird ausgeführt
@Testeine Methodeeinmal pro Testmethode
@BeforeEacheine Methodevor jedem @Test
@AfterEacheine Methodenach jedem @Test
@BeforeAlleine static-Methodeeinmal, vor allen Tests in der Klasse
@AfterAlleine static-Methodeeinmal, nach allen Tests in der Klasse
@Disabledeine Methode oder Klassenie (sie wird übersprungen und gemeldet)
@DisplayNameeine Methode oder Klassesetzt 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.

java— editable, runs on the server

Was man aus dem Durchlauf mitnehmen kann:

  • @BeforeAll wurde genau einmal ganz oben ausgegeben und @AfterAll genau einmal ganz unten — das Setup und Teardown auf Klassenebene umrahmt den gesamten Durchlauf, weshalb JUnit verlangt, dass sie static sind.
  • Jedem ausgeführten @Test geht 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.

Übungen

Übung
Warum muss in JUnit 5 eine mit @BeforeAll annotierte Methode als static deklariert werden?
Warum muss in JUnit 5 eine mit @BeforeAll annotierte Methode als static deklariert werden?
Was this page helpful?