GEDOPLAN
JUnitJavaJava - Software Testing

JUnit 6 ist da – und keiner hat’s gemerkt

JUnitJavaJava - Software Testing
JUnit 6 ist da 1 png

Und das hat einen Grund: Es ist nicht die radikale Neuerfindung, sondern das Release der Modernisierung, Stabilisierung und Vereinheitlichung.

Die Java-Mindestversion wurde von Java 8 auf 17 angehoben, alle Dependencies haben nun die gleiche Versionsnummer, vorhandene Features sind für Entwickler leichter einsetzbar, und überhaupt ist alles runder.

Werfen wir einen Blick auf die wichtigsten Neuerungen.

Java 17 als Mindestvoraussetzung

JUnit 6 setzt mindestens Java 17 voraus, Java 8 wird nicht mehr unterstützt. Java 11 auch nicht. Die Unterstützung für die älteren Java-Versionen wurde komplett entfernt.

Dadurch kann JUnit intern moderne Sprach- und Plattformfeatures nutzen und muss keine Kompatibilitätsschichten für veraltete Java-Versionen mehr mitführen. Für Anwender bedeutet das vor allem: Wer JUnit 6 einsetzen möchte, muss sein Projekt auf Java 17 oder neuer betreiben.

Vereinheitlichte Versionierung

JUnit 6 verwendet für alle Module dieselbe Versionsnummer. Bisher hatten JUnit Platform (1.x) und JUnit Jupiter (5.x) unterschiedliche Versionen. Jetzt läuft alles unter einer einheitlichen Versionsnummer 6.x. Dadurch wird das Dependency Management einfacher, insbesondere in Verbindung mit der JUnit BOM, da alle JUnit-Abhängigkeiten automatisch in einer zueinander passenden Version verwendet werden.

Verbesserte CSV-Unterstützung für parametrisierte Tests

Die alte Bibliothek univocity-parsers, die JUnit bisher für @CsvSource verwendet hat, wird nicht mehr gewartet. JUnit 6 verwendet nun FastCSV. Dadurch werden CSV-Daten schneller und RFC-4180-konform verarbeitet. Mehr Performance, präzisere Fehlermeldungen.

Zusätzlich unterstützt @CsvSource nun Zeichenfolgen als Trennzeichen (delimiterString) sowie Java-Textblöcke, wodurch größere Testdatensätze deutlich lesbarer werden.

public class CsvParameterizedTest {
  @ParameterizedTest
  @CsvSource(
      delimiterString = "::",
      textBlock = """
          Alice::42
          Bob::nineteen
          Charlie::35
          """
  )
  void csv(String name, int age) {
    System.out.println(name + " -> " + age);
    assertTrue(age > 0);
  }
}

Die Ausgabe sieht so aus:

grafik png

Wie man sieht, schlägt Bob fehl, da „nineteen“ kein gültiger Integer ist. Dank FastCVS ist die Fehlermeldung dabei jetzt noch präziser.

Übersichtlich und einfach nachvollziehbar. Gerade bei umfangreichen parametrisierten Tests verbessert dies die Wartbarkeit und erleichtert die Fehlersuche.

JRE-Bedingungen (JRE Conditions) für Java-Versionen

Mit JRE-Bedingungen können Tests gezielt nur unter bestimmten Java-Versionen oder Versionsbereichen ausgeführt werden. Typische Anwendungsfälle sind Tests für neue Sprach- oder Plattformfeatures, die erst ab einer bestimmten Java-Version verfügbar sind, oder Bibliotheken, die mehrere Java-Versionen unterstützen.

  @Test
  @EnabledOnJre(JRE.JAVA_17)
  void runsOnlyOnJava17() {
    System.out.println("✓ Running on Java 17 only");
  }

  @Test
  @EnabledOnJre(JRE.JAVA_25)
  void runsOnlyOnJava25() {
    System.out.println("✓ Running on Java 25 only");
  }

  @Test
  @DisabledOnJre(JRE.JAVA_25)
  void runsOnEverySupportedJavaExcept25() {
    System.out.println("✓ Running because this is NOT Java 25");
  }

  /**
   * Typical real-world example: A feature (e.g. Virtual Threads) is available only from Java 21 onwards.
   */
  @Test
  @EnabledForJreRange(min = JRE.JAVA_21)
  void runsOnJava21AndNewer() {
    System.out.println("✓ Running on Java 21+");
  }

  /**
   * Example for libraries that officially support a range of Java versions.
   */
  @Test
  @EnabledForJreRange(
      min = JRE.JAVA_17,
      max = JRE.JAVA_25
  )
  void runsFromJava17ThroughJava25() {
    System.out.println("✓ Running on Java 17..25");
  }

JUnit 6 modernisiert diese API entsprechend der neuen Mindestvoraussetzung Java 17 und entfernt die Unterstützung für nicht mehr unterstützte Java-Versionen.

JSpecify-Integration für Null-Sicherheit

Die öffentlichen APIs von JUnit 6 sind mit JSpecify-Annotationen versehen, die angeben, welche Parameter und Rückgabewerte null sein dürfen und welche nicht. Echte Typsicherheit zur Compilezeit statt NullPointerExceptions zur Laufzeit.

@Test
void validExecutable() {
  Executable executable = () -> System.out.println("Hello JUnit 6");
  assertDoesNotThrow(executable);
}

@Test
void accidentalNullExecutable() {
  // Simulate a bug
  Executable executable = null;

  // Compiles...
  // ...but IntelliJ/Eclipse using JSpecify can warn:
  // "Argument 'executable' is marked @NonNull"

  assertThrows(RuntimeException.class, executable);
}

Entwicklungsumgebungen und statische Analysewerkzeuge, die JSpecify unterstützen, können dadurch fehlerhafte Verwendungen der JUnit-API bereits während der Entwicklung erkennen. Wird beispielsweise ein null-Wert als Parameter übergeben, der nicht null sein darf, kann die IDE eine Warnung anzeigen.

grafik 1 png

Der Nutzen hängt davon ab, ob das verwendete Werkzeug JSpecify bereits unterstützt. Am Laufzeitverhalten von Tests ändert sich dadurch nichts.

Und sonst?

JFR-Integration

Früher gab es ein separates Modul (junit-platform-jfr). In JUnit 6 ist die Flight-Recorder-Unterstützung direkt in den Core (junit-platform-launcher) gewandert. Jetzt lässt sich bei der Testausführung (z. B. via Build-Tool oder CI) direkt ein JFR-Recording starten, um Performance-Engpässe oder Speicherprobleme bei großen Test-Suiten zu analysieren.

Cancellation API

Laufende Tests können nun über ein CancellationToken mitten im Run abgebrochen werden. Das kann für CI/CD-Pipelines nützlich sein.

Aufgeräumte Module

Eine Warnung für alle, die von uralten Versionen wechseln möchten: Ein paar alte Zöpfe wurden abgeschnitten. junit-platform-runner wurde ersatzlos gestrichen, junit-platform-jfr ist direkt in den Laucher gewandert (siehe voriger Punkt). Außerdem wurde das Migrationsmodul junit-jupiter-migrationsupport als deprecated markiert.

Entscheidungshilfe: Lohnt sich der Umstieg auf JUnit 6?

Der Umstieg ist kein „Sofort-erledigen!“-Notfall, aber für viele Teams ein logischer nächster Schritt. Hier eine Orientierungshilfe.

Wann der „Umstieg jetzt“ sinnvoll ist

  • Das Projekt arbeitet bereits mit Java 17, 21 oder 25 und modernem Tech-Stack. Dann gibt es keinen Grund, nicht auch JUnit auf Version 6 anzuheben.
  • Die Build-Dateien wollen endlich mal wieder aufgeräumt werden. Wenn das Versionsdurcheinander der verschiedenen JUnit-Komponenten schon immer gestört hat, bringt JUnit 6 hier endlich Ordnung rein.

Wann „warten“ okay ist

  • Das Projekt hängt aus Gründen bei Java 8 oder 11 fest. JUnit 6 setzt mindestens Java 17 voraus, und solange das Hauptprojekt nicht migriert wurde, bleibt man auf JUnit 5, notgedrungen. JUnit 5 ist stabil, gut abgehangen und erhält auch weiterhin Support.
  • Es gibt wichtigere Baustellen. Das Projekt läuft auf Java 17+, aber parametrisierte Tests werden kaum angefasst werden und Kotlin-Coroutinen nicht genutzt, dann brächte ein Umstieg zwar Performance- und Lesbarkeitsvorteile im Detail, aber nichts Revolutionäres, für das man alles stehen und liegen lassen muss.

Tipps für den Umstieg

Der Umstieg von JUnit 5 auf 6 ist zwar wie alles, was „mal schnell“ gemacht werden können soll, nicht ohne Aufwand, aber eher als sanftes technisches Upgrade denn einen kompletten und schmerhaften Rewrite zu sehen.

  • Offensichtlich, aber trotzem erwähnenswert: Das Projekt muss mindestens auf Java 17 angehoben sein.
  • Da JUnit 6 auf die einheitliche Versionierung setzt, können die alten, separaten Versionsvariablen (wie junit.jupiter.version vs. junit.platform.version) aus der Build-Konfiguration entfernt werden. Es gibt nur noch eine globale JUnit-Version.
  • Alte Migrations-Module: Falls das Projekt noch sehr alte JUnit-4-Tests mitschleppt und dafür junit-jupiter-migrationsupport nutzt, ist jetzt der perfekte Zeitpunkt, diese alten Tests endlich auf JUnit 6-Syntax umzuschreiben.
  • Immer gern zu hören: Ein schrittweiser Umstieg ist möglich! Da die @Test-, @BeforeEach– und @AfterEach-Annotationen und deren Verhalten identisch geblieben sind, reicht es in vielen Fällen aus, einfach die Versionsnummer in der pom.xml oder build.gradle hochzusetzen.

Fazit

JUnit 6 führt keine grundlegend neue Art des Testens ein. Stattdessen modernisiert das Release die bestehende Plattform:

  • Java 17 als zeitgemäße Basis
  • Vereinfachtes Dependency Management
  • Bessere Unterstützung durch IDEs und statische Analyse
  • Lesbarere parametrisierte Tests
  • Reproduzierbarere Testausführung
  • Bessere Integration in moderne Build- und CI-Prozesse

Der Schwerpunkt liegt damit weniger auf revolutionären Test-Features als auf einer verbesserten Entwicklererfahrung und einer Modernisierung des JUnit-Ökosystems. Wer moderne Projekte pflegt, nimmt das Upgrade einfach beim nächsten Sprint-Wechsel dankend mit.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Bitte füllen Sie dieses Feld aus.
Bitte füllen Sie dieses Feld aus.
Bitte gib eine gültige E-Mail-Adresse ein.
Sie müssen den Bedingungen zustimmen, um fortzufahren.

Autor

Diesen Artikel teilen

LinkedIn
Xing

Gibt es noch Fragen?

Fragen beantworten wir sehr gerne! Schreibe uns einfach per Kontaktformular.

Kurse

weitere Blogbeiträge

tailwind ng png
Angular, Webprogrammierung

Angular + Tailwind

In den meisten meiner Angular Projekte kommt @angular/material als Bibliothek zum Einsatz, um die „handelsüblichen“ Komponenten parat zu haben, die…

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!

Work Life Balance. Jobs bei Gedoplan

We are looking for you!

Lust bei GEDOPLAN mitzuarbeiten? Wir suchen immer Verstärkung – egal ob Entwickler, Dozent, Trainerberater oder für unser IT-Marketing! Schau doch einfach mal auf unsere Jobseiten! Wir freuen uns auf Dich!