Mit Version 5 von JUnit wird das Testen unter Java noch einfacher. Hier ein paar Beispiele …
Eines vorweg, das Testen wird einfacher, die Installation wurde durch die Modularisierung etwas komplizierter. In der JUnit-Dokumentation wird im Abschnitt Installation in einem Diagramm 15 (!) voneinander abhängige Komponenten dargestellt. Auf GitHub gibt es Beispielprojekte für verschiedene Programmiersprachen und Build-Werkzeuge.
Alle Details zu den folgenden Beispiel können in der ausführlichen Dokumentation auf der Webseite von JUnit nachgelesen werden.
Das vollständige Beispiel der nachfolgenden Codeschnipsel liegt bei mir auf GitHub.
Gradle-Konfiguration
Für Gradle und Java sieht die Konfiguration so aus:
plugins { id 'java' } sourceCompatibility = 11 repositories { mavenCentral() } dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.3.2' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.3.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.3.2' } test { useJUnitPlatform() }
Wie man sieht, sind tatsächlich nicht alle 15 Komponenten wirklich als Abhängigkeit notwendig. Sollen keine datengetriebenen Tests durchgeführt werden, kann auf das Artefakt junit-jupiter-params
verzichtet werden.
Für CI-Builds zum Beispiel nützlich, sind hilfreiche Ausgaben auf der Konsole, vor allem bei Fehlern. Diese lassen sich erzeugen mit der testLogging
-Erweiterung des Test-Blocks:
test { useJUnitPlatform() testLogging { events 'passed', 'skipped', 'failed' exceptionFormat = 'full' showStackTraces = false } }
Einfache Tests
Einfache Test sind mit JUnit 4 und 5 gleich:
@Test void convert() throws ParseException { assertEquals(4, RomanConverter.fromRoman("IV")); }
Lediglich die Pakete der zu importierenden Annotationen und Asserts hat sich geändert.
Datengetriebene Tests
Einfacher geworden sind datengetriebene Tests. Wofür bisher eine eigene Klasse notwendig war, können datengetrieben Test mit JUnit 5 als Methode definiert werden. Die Testparameter werden nun einfach mit einer Annotation festgelegt.
@ParameterizedTest @CsvSource({ "M, 1000", // nur eine römische Ziffer "XVI, 16", // nur Addition von Ziffernwerten "MCDXCII, 1492", "MCMLXXXIV, 1984", }) void fromRomanNumerals(String romanNumeral, int translation) throws ParseException { assertEquals(translation, RomanConverter.fromRoman(romanNumeral)); }
Eine Tabelle mit Eingangsdaten und Ausgangsdaten kann als CSV-Daten direkt in die Annotation @CsvSource
notiert werden. Die CSV-Daten können auch als Resource im Classpath geladen werden: @CsvFileSource(resources = "/testdaten.csv", numLinesToSkip = 1)
. Alternativ gibt es weitere Annotationen, zum Beispiel für eine Fabrikmethode eines Streams von Parametern.
Ausnahmeverhalten testen
Statt mit einer Rule
, können nun Ausnahmen direkt in der Testmethode geprüft werden. Dafür wird der Testcode in einem Lambda-Ausdruck ausgeführt. Das Assert, das diesen Lambda-Ausdruck ausführt, gibt die geworfene Exception
zurück, die dann weiter untersucht werden kann. Wird die erwartete Ausnahme nicht geworfen, schlägt das Assert fehl.
@Test void notARomanNumeral() { ParseException exception = assertThrows(ParseException.class, () -> { RomanConverter.fromRoman("XAI"); }); assertEquals("not a roman digit: A", exception.getMessage()); assertEquals(1, exception.getErrorOffset()); }
Benennen von Tests
Da die Testmethoden nicht immer gute Namen abgeben, können für Testprotokolle alternative Namen angegeben werden.
Für allgemeine Tests, wird dafür die Annotation @DisplayName
verwendet.
@Test @DisplayName("XAI is not a roman numeral") void notARomanNumeral() { [...] }
Für datengetriebene Tests wird die Annotation verwendet.
@ParameterizedTest(name = "[{index}] Convert from roman numeral {0} to {1}") @CsvSource({ [...] }) void fromRomanNumerals(String romanNumeral, int translation) throws ParseException { [...] }
Das Ergebnis im Testprotokoll von Gradle sieht dann zum Beispiel so aus: