Start / Seminare / Java Testing mit JUnit, Mockito und Testcontainers
Modul
JUnit 6 verstehen und einrichten
4 Kapitel in diesem Modul-Video · Laufzeit
Transkript
Der gesprochene Text dieses Moduls zum Mitlesen, Überfliegen und Durchsuchen. Ein Klick auf einen Zeitstempel springt an die Stelle im Video.
JUnit 6 verstehen und einrichten
0:00 Im zweiten Modul geht es um das Fundament: JUnit 6. Und ich sage gleich dazu, was Sie nicht erwartet — eine lange Liste neuer Annotationen. JUnit 6 ist keine Revolution, sondern ein Aufräumen. Das Programmiermodell bleibt, die Java-Baseline steigt auf 17, und die Versionsnummern von Platform, Jupiter und Vintage laufen endlich im Gleichschritt.
0:21 Genau deshalb lohnt es sich, den Aufbau einmal zu verstehen: Wer weiß, welches Teil welche Aufgabe hat, kann drei Klassen von Fehlermeldungen sofort einordnen — und die begegnen einem sonst immer wieder.
JUnit 6 verstehen und einrichten
0:33 Wir bleiben am ersten Tag und wechseln von der Strategie zur Mechanik. Zuerst der Aufbau von JUnit 6, dann die Einbindung in Maven und Gradle, und zum Schluss die Migration bestehender Testbestände. Das ist der Teil, den man einmal richtig macht und danach jahrelang nicht mehr anfasst.
Aufbau von JUnit 6
0:50 Beginnen wir mit der Frage, warum JUnit überhaupt aus drei Teilen besteht — und warum das keine historische Altlast ist, sondern eine sinnvolle Trennung. Stellen Sie sich einen Bahnhof vor. Die JUnit Platform ist der Bahnhof: Sie kennt die Gleise und den Fahrplan, fährt aber selbst nirgendwohin. Die Züge sind die Test Engines — Jupiter für moderne Tests, Vintage für die alten aus JUnit 3 und 4. Und Ihre Tests sind die Fahrgäste.
1:17 Der Vorteil dieser Trennung: Ihre IDE und Ihr Build müssen nur den Bahnhof kennen, nicht jeden Zugtyp. Neu in JUnit 6 ist, dass alle drei dieselbe Versionsnummer tragen — das beendet eine ganze Familie von Versionskonflikten. Zur Laufzeit braucht JUnit 6 mindestens Java 17. Lesen Sie diese Schichtung von unten nach oben. Build und IDE sprechen ausschließlich mit der Platform — sie kennen Jupiter gar nicht.
1:44 Die Platform sucht ihrerseits nach Engines und fragt jede, ob sie passende Tests findet. Erst die Engine kennt Ihre Testklassen. Der praktische Nutzen dieser Kette: Sie können eine neue Engine ergänzen, ohne Build oder IDE anzufassen. Der praktische Nachteil: Wenn ein Glied fehlt, meldet niemand einen Fehler. Die Platform findet dann einfach nichts — und ein Testlauf ohne Tests ist grün.
2:08 Genau darum geht es hier. Die häufigste rätselhafte Meldung in JUnit-Projekten lautet sinngemäß: keine Tests gefunden. Das ist kein Fehler, sondern eine korrekte Auskunft — die Platform hat gesucht und nichts gefunden, meist weil die Engine fehlt oder nur zur Compile-Zeit da ist. Jupiter und Vintage können übrigens problemlos nebeneinander laufen, jede findet nur ihre eigenen Tests.
2:31 Der letzte Punkt ist der wichtigste im ganzen Modul: Ein Test ohne passende Engine ist kein roter Test, sondern ein unsichtbarer. Und unsichtbare Tests sind gefährlicher als fehlende. Diese Tabelle wirkt nach Buchhaltung und erspart Ihnen trotzdem einen halben Tag. Wichtig ist die mittlere Spalte: In JUnit 6 trägt nur die BOM die Gruppe org.junit, die Komponenten behalten ihre eigenen Gruppen.
2:56 Wer das übersieht, sucht das Artefakt an der falschen Stelle. Zwei Zeilen verdienen besondere Aufmerksamkeit. Der Launcher wird oft vergessen, weil die IDE ihn mitbringt — im Build fehlt er dann. Und die Vintage Engine sollte nur so lange im Projekt stehen, wie es tatsächlich JUnit-4-Tests gibt. Sie ist eine Übergangshilfe, kein Dauerbestandteil.
3:18 Der erste Punkt trifft ältere Projekte hart: JUnit 6 verlangt Java 17, und wer auf 11 bleibt, kommt nicht mit. Der zweite ist ein Klassiker mit Folgen — steht die Engine als compile-Abhängigkeit im Projekt, landet Testinfrastruktur im Auslieferungsartefakt. Der dritte ist der Grund für viele verlorene Stunden: In der IDE laufen die Tests, im Build nicht, weil dort der Launcher fehlt.
3:42 Und der vierte ist reine Trägheit — Vintage bleibt eingebunden, obwohl längst kein JUnit-4-Test mehr existiert. Das kostet keinen Fehler, aber es hält eine Brücke offen, die niemand mehr braucht.
JUnit in den Build einbinden
3:54 Vom Aufbau zur Praxis: Wie kommt JUnit in ein Maven- oder Gradle-Projekt, ohne dass man später Versionen jagt? Die Antwort ist erfreulich kurz. Eine BOM — eine Bill of Materials — ist im Grunde eine Einkaufsliste mit festen Preisen. Sie sagt einmal, welche Version für alle JUnit-Artefakte gilt, und danach schreiben Sie nirgends mehr eine Versionsnummer.
4:17 Das klingt nach Kosmetik, verhindert aber eine ganze Fehlerklasse: eine Jupiter-API in einer Version gegen eine Engine in einer anderen. Solche Konflikte fallen nicht beim Bauen auf, sondern erst zur Laufzeit, mit Meldungen, die nichts mit der Ursache zu tun haben. Und das Zweite, was Sie hier mitnehmen: JUnit wird über Parameter konfiguriert, nicht über Code.
4:39 Entscheidend an diesem Block sind nicht die Zeilen, sondern zwei Details. Erstens der Typ pom mit dem Geltungsbereich import — dadurch wird die BOM nicht als Abhängigkeit eingebunden, sondern nur ihre Versionsangaben werden übernommen. Zweitens der Ort: Das gehört in den Abschnitt für die Abhängigkeitsverwaltung, nicht zu den normalen Abhängigkeiten.
4:59 Danach stehen die eigentlichen JUnit-Einträge ohne jede Versionsnummer im Projekt. Der Gewinn zeigt sich beim nächsten Upgrade: Sie ändern genau eine Zeile, und alles zieht geschlossen mit. Ohne BOM sind es erfahrungsgemäß vier bis sechs Stellen, und eine wird vergessen. In Gradle heißt das Mittel platform, das Prinzip ist identisch. Drei Dinge sind hier bemerkenswert. Der Aufruf platform importiert die Versionsangaben.
5:24 Die Jupiter-Abhängigkeit kommt danach ohne Version. Und die dritte Zeile ist die, die am häufigsten fehlt: der Launcher, und zwar nur zur Laufzeit der Tests. Genau dieser Eintrag entscheidet darüber, ob Gradle Ihre Tests findet oder achselzuckend meldet, es gebe keine. Wer diese Zeile einmal bewusst gesetzt hat, erkennt das Symptom beim nächsten Projekt in zehn Sekunden wieder.
5:48 Diese vier Parameter sind nur eine Auswahl, aber sie zeigen das Prinzip: Verhalten, das das ganze Projekt betrifft, wird konfiguriert und nicht in jede Testklasse geschrieben. Die parallele Ausführung sehen wir uns im fünften Modul genauer an, den Testinstanz-Lebenszyklus ebenfalls. Wichtig ist heute vor allem der Ort: Die Parameter stehen in einer Datei namens junit-platform.properties, und die muss im Testklassenpfad liegen.
6:12 Landet sie im Hauptquellbaum, wirkt sie nicht — und weil auch keine Meldung erscheint, sucht man an der falschen Stelle. Tags sind ein unterschätztes Werkzeug. Sie markieren Tests mit einem Etikett, und der Build wählt aus. Damit trennen Sie die schnelle Menge, die bei jedem Pull Request läuft, von der vollständigen, die nachts läuft — ohne eine Zeile Testcode zu ändern.
6:34 Test Suites gehen noch einen Schritt weiter und bündeln Tests über Klassen und Module hinweg. Der letzte Punkt ist der eigentliche Grund für diese Folie: Ohne Tags entscheidet allein die Laufzeit darüber, was noch in die Pipeline passt. Und diese Entscheidung fällt dann unter Termindruck. Der erste Punkt ist die BOM, die zur Zierde dasteht: Sie ist eingebunden, aber jede Abhängigkeit trägt trotzdem ihre eigene Version — dann hat sie keinerlei Wirkung.
7:00 Der zweite ist die Konfigurationsdatei im falschen Quellbaum, die stillschweigend nichts tut. Der dritte ist gut gemeint: Tags werden vergeben, aber kein Build filtert danach; dann sind sie Dokumentation. Und der vierte ist der Klassiker aus dem letzten Kapitel — der Launcher fehlt, Gradle meldet lapidar, es gebe keine Tests, und der Build ist grün.
7:21 Prüfen Sie im Zweifel immer, wie viele Tests wirklich gelaufen sind.
Migration bestehender Testbestände
7:25 Die wenigsten von uns starten auf der grünen Wiese. Sehen wir uns an, wie ein bestehender Testbestand nach JUnit 6 kommt — und warum der Weg von JUnit 4 anders aussieht als der von JUnit 5. Die gute Nachricht zuerst: Von JUnit 5 auf 6 ist es überwiegend eine Versionsanhebung. Gleiches Programmiermodell, gleiche Annotationen, gleiche Assertions.
7:47 Der eigentliche Aufwand steckt in der neuen Baseline Java 17 — und der betrifft nicht die Tests, sondern das Projekt. Bei JUnit 4 ist es ein anderer Weg: Diese Tests laufen über die Vintage Engine weiter, ohne dass Sie eine Zeile ändern. Das ist bequem und genau deshalb eine Falle. Vintage ist als Übergangshilfe gedacht, nicht als Dauerzustand — und Übergänge, die kein Ende haben, hören auf, Übergänge zu sein.
8:13 Der rote Faden dieser Schritte ist: erst die Umgebung, dann die Abhängigkeiten, dann der Code. Deshalb steht die Java-Baseline an erster Stelle — wer bei den Testklassen anfängt, hat später beide Baustellen gleichzeitig offen. Danach kommt die BOM, und die Versionsangaben verschwinden. Schritt drei betrifft Module, die es in JUnit 6 nicht mehr gibt, etwa den alten JUnit-4-Runner der Platform.
8:37 Schritt vier und fünf gehören zusammen: Vintage rein, aber mit einem Enddatum. Ohne dieses Datum steht Vintage in zehn Jahren noch im Projekt, das kann ich Ihnen versprechen. Diese Tabelle sieht nach Vokabelheft aus, und der größte Teil davon ist es auch — die Umbenennungen erledigt ein Migrationswerkzeug in wenigen Minuten.
8:58 Interessant ist die dritte Zeile: Aus Runner und Rule wird die Extension. Das ist keine Umbenennung, sondern ein anderes Konzept, und diese Umstellung bleibt Handarbeit. Genau daran hängt in der Praxis die Migration. Wenn Sie also abschätzen wollen, wie aufwendig der Umstieg wird, zählen Sie nicht die Tests, sondern die eigenen Rules und Runner.
9:18 Deren Zahl ist die ehrlichere Schätzgrundlage. Der springende Punkt: Vintage führt alte Tests aus, aber es macht sie nicht besser. Rules und Runner bleiben JUnit-4-Konzepte, und sie lassen sich nicht mit Extensions kombinieren. Solange Vintage läuft, leben zwei Programmiermodelle im selben Projekt — mit zwei Sätzen von Annotationen, zwei Erweiterungsmechanismen und zwei Erklärungen für neue Kolleginnen und Kollegen.
9:43 Das ist für eine Übergangszeit völlig in Ordnung. Wer die Brücke aber nicht abbaut, zahlt bei jedem Werkzeugwechsel doppelt — und beim nächsten großen Sprung steht die Migration dann zweimal an. Der erste Punkt haben wir eben gesehen: Wer bei den Testklassen beginnt statt bei der Java-Baseline, arbeitet gegen sich selbst.
10:02 Der zweite ist ein Überraschungsfund — ein eigener Runner irgendwo im Bestand, der erst in der Pipeline auffällt. Der dritte ist Bequemlichkeit mit Preis: Vintage für drei verbliebene Tests einzubinden ist teurer, als diese drei zu übersetzen. Und der vierte ist der inhaltlich wichtigste: Alte Tests werden übersetzt statt überdacht.
10:22 Dabei wandern ihre Schwächen unverändert mit — inklusive der Tests, die nie eine Assertion hatten.
Übung
10:28 Genug Theorie. Wir richten den Testbau für Krautgut ein und schauen uns an, was ein realer Altbestand an Überraschungen bereithält. Ihre Aufgabe hat zwei Teile. Der erste ist Handwerk: BOM einbinden, Jupiter und Launcher ergänzen, Versionsangaben entfernen — und zwar so, dass IDE und Kommandozeile identisch laufen. Genau dieses „identisch" ist das Erfolgskriterium, und es wird erstaunlich oft verfehlt.
10:54 Der zweite Teil ist eine Entscheidung: Im mitgelieferten Zweig stecken acht JUnit-4-Tests aus der ersten Krautgut-Version. Sie sollen nicht alle übersetzen, sondern eine Ablösereihenfolge festlegen. Damit üben Sie die Frage, die bei jeder Migration wirklich zählt — womit fängt man an, wenn nicht alles auf einmal geht? Schritt eins ist reine Konfiguration.
11:16 Schritt zwei ist der, den man gerne überspringt und nicht überspringen sollte: einmal aus der IDE starten, einmal aus dem Build, und die Zahl der gelaufenen Tests vergleichen. Wer das jetzt tut, findet den fehlenden Launcher in dreißig Sekunden statt später in der Pipeline. Schritt drei bringt Vintage ins Spiel — und Sie werden sehen, dass nicht alle acht Altlasten einfach so durchlaufen.
11:39 Schritt vier ist die eigentliche Denkarbeit: nach Aufwand sortieren und die Reihenfolge festhalten. Am besten mit Datum. Die ersten beiden Punkte kennen Sie schon, sie treten hier praktisch auf: die Konfigurationsdatei im falschen Quellbaum und Vintage ohne Endtermin. Der dritte ist der gefährlichste in dieser Übung — der Build zeigt grün und führt null Tests aus. Deshalb der Rat: Schauen Sie immer auf die Zahl, nicht auf die Farbe.
12:05 Und der vierte betrifft die Altlasten: Alte Rules lassen sich nicht nach Jupiter kopieren, sie müssen als Extension neu geschrieben werden. Wie das geht, sehen wir im fünften Modul — dort bauen wir unsere erste eigene Extension.
Lieber mit Trainer? Dieses Modul ist Teil unserer Team-Schulungen — mit Übungen, Ihrem eigenen Code und Fragen, die ein Video nicht beantwortet. Mehr erfahren →