Parametrisierte Tests: Unterschied zwischen den Versionen
(→Enums) |
Keine Bearbeitungszusammenfassung |
||
(2 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
Zeile 22: | Zeile 22: | ||
weit über das hinausgeht was JUnit-4 oder Test-NG zu bieten hatten. Eine Vielzahl von Test-Situationen läßt sich | weit über das hinausgeht was JUnit-4 oder Test-NG zu bieten hatten. Eine Vielzahl von Test-Situationen läßt sich | ||
damit abdecken und vor allem eindampfen. | damit abdecken und vor allem eindampfen. | ||
==Eine Liste einzelner Parameter== | == Eine Liste einzelner Parameter == | ||
Betrachten wir –- angelehnt an das einleitende Beispiel -– die einfachste Situation: Einen Testfall soll mit einer Reihe | Betrachten wir –- angelehnt an das einleitende Beispiel -– die einfachste Situation: Einen Testfall soll mit einer Reihe | ||
einzelner Strings aufgerufen werden. Dazu parametrisieren wir zunächst die Test-Methode und ziehen den Datum-String als Parameter heraus: | einzelner Strings aufgerufen werden. Dazu parametrisieren wir zunächst die Test-Methode und ziehen den Datum-String als Parameter heraus: | ||
{{java|code= | {{java|code= | ||
void toDateLiefertKorrektesDatum(String datum) { | |||
Date convertedDate = Convert.toDate(datum); | Date convertedDate = Convert.toDate(datum); | ||
assertThat(convertedDate).hasDayOfMonth(1); | assertThat(convertedDate).hasDayOfMonth(1); | ||
Zeile 34: | Zeile 34: | ||
}} | }} | ||
Dann legen wir die Datum-Strings fest mit denen getestet werden soll: | Dann legen wir die Datum-Strings fest mit denen getestet werden soll: | ||
{{java|"1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985"}} | {{java|"1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985"}}. | ||
Um die neue Test-Methode mit den Test-Fälle zu verknüpfen verwenden wir JUnit5- | Um die neue Test-Methode mit den Test-Fälle zu verknüpfen, verwenden wir JUnit5-Annotationen und setzen sie | ||
über unsere parametrisierte Test-Methode: | über unsere parametrisierte Test-Methode: | ||
{{java|code= | {{java|code= | ||
Zeile 41: | Zeile 41: | ||
@ValueSource(strings = { "1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985" }) | @ValueSource(strings = { "1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985" }) | ||
}} | }} | ||
Die erste Annotation ersetzt die @Test-Annotation und macht die Methode zum (parametrisierten) Test. | Die erste Annotation ersetzt die {{java|@Test}}-Annotation und macht die Methode zum (parametrisierten) Test. | ||
Die zweite Annotation legt die Quelle der zu verwendenden Werte fest. | Die zweite Annotation legt die Quelle der zu verwendenden Werte fest. | ||
In diesem Falle ist dies eine Liste von Strings. Der Test wird bei der | In diesem Falle ist dies eine Liste von Strings. Der Test wird bei der | ||
Zeile 47: | Zeile 47: | ||
Anstelle von Strings sind hier auch alle numerischen, primitiven Typen int, long, float und einiges mehr erlaubt. | Anstelle von Strings sind hier auch alle numerischen, primitiven Typen int, long, float und einiges mehr erlaubt. | ||
Die Anntotations-Parameter kann man in Eclipse mit Code-Assist erkunden oder in der JUnit5-Doku nachlesen. | Die Anntotations-Parameter kann man in Eclipse mit Code-Assist erkunden oder in der JUnit5-Doku nachlesen. | ||
=== | |||
Beachtenswert ist, daß Annotationen nur Konstanten als Argumente übernehmen, | |||
also Ausdrücke die zur Compile-Zeit ausgewertet werden können. Objekte zu übergeben ist so nicht möglich. | |||
Wie man damit umgeht, sehen wir in den folgenden Abschnitten. | |||
Vorher aber noch ein Wort zu {{java|null}}. | |||
=== Was ist mit null? === | |||
Die eben vorgestellte Werte-Liste hat einen kleinen Haken: Es ist nicht möglich, den Wert null als Argument zu | Die eben vorgestellte Werte-Liste hat einen kleinen Haken: Es ist nicht möglich, den Wert null als Argument zu | ||
verwenden. Um einen Testfall für null zu erzeugen, kann man nun freilich eine neue Test-Methode erstellen und | verwenden. Um einen Testfall für null zu erzeugen, kann man nun freilich eine neue Test-Methode erstellen und | ||
null separat in diesem Test prüfen. Man kann dem Test aber auch zusätzlich die Annotation @NullSource | null separat in diesem Test prüfen. Man kann dem Test aber auch zusätzlich die Annotation {{java|@NullSource}} | ||
hinzufügen. JUnit5 erzeugt dann einen zusätzlichen Testfall für null: | hinzufügen. JUnit5 erzeugt dann einen zusätzlichen Testfall für {{java|null}}: | ||
{{java|code= | {{java|code= | ||
@ParameterizedTest | @ParameterizedTest | ||
Zeile 60: | Zeile 67: | ||
} | } | ||
}} | }} | ||
Unterstützt wird {{java|@NullSource}} ab Version 5.4 | Unterstützt wird {{java|@NullSource}} ab Version 5.4. | ||
== Einzelne Argumente aus einer Methode == | |||
Anstelle der Angabe von Annotations-Parametern kann man eine Methode schreiben die die Argumente liefert. | |||
Die Methode muß {{java|static}} sein und einen {{java|Stream}} von "irgendwas" liefern. | |||
Das obige Beispiel kann man dann so schreiben: | |||
{{java|code= | |||
@ParameterizedTest | |||
@MethodSource("testDatenMethode") | |||
void toDateLiefertKorrektesDatum(String datum) { | |||
// sieht genauso aus wie oben | |||
} | |||
static Stream<String> testDatenMethode() { | |||
return Stream.of("1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985"); | |||
}} | |||
Das Beispiel hat den gleichen Effekt wie die Implementierung im vorabgegangenen Abschnitt, | |||
aber die Möglichkeiten sind offensichtlich viel weiterreichend. In der Test-Daten-Methode lassen | |||
sich beliebige Objete liefern, die bei bedarf auch dynamisch erzeugt werden können. | |||
Der Wert {{java|null} ist hier kein Problem, er darf im Stream enthalten sein. | |||
Hat die Testdaten-Methode den gleichen Namen wie die parameterisierte Test-Methode, | |||
kann man den Parameter der Annotation {{java|@MethodSource}} übrigens weglassen. | |||
== Mehrere Parameter für die Test-Methode == | == Mehrere Parameter für die Test-Methode == | ||
Zeile 117: | Zeile 147: | ||
daß die Method nicht verwendet wird wenn sie als private deklariert wurde. | daß die Method nicht verwendet wird wenn sie als private deklariert wurde. | ||
=== Lambda-Ausdrücke === | === Lambda-Ausdrücke === | ||
Warum sollte man {{lambda}}-Ausdrücke in einem parametrisierten Test verwenden? | |||
Denkt man etwa an ein Objekt mit einer Reihe von getter- und setter-Methoden, | |||
läßt sich mithilfe von {{lambda}}-Ausdrücken die Objekt-Bestückung sehr kompakt vertesten. | |||
ersetzt man in der oben vorgestellten Befüll-Methode ein String-Argument durch einen {{lambda}}-Ausdruck | |||
läuft der compiler allerdings ganz flott auf einen Fehler, weil der {{lambda}}-Ausdruck nicht in ein Objekt | |||
gecastet werden kann. Dazu braucht es eines (funktionalen) Interfaces. Man kann dazu die vom JDK bereitgestellten | |||
Interfaces nutzen oder eigene Interfaces definieren. Für Unit-Tests ist das zweitere vorzuziehen. | |||
Stellen wir uns also mal eine POJO mit dem klassischen Getter und Setter vor: | |||
{{java|code= | |||
public class Person { | |||
private String vorname; | |||
}} | |||
==Weitere Test-Daten-Provider== | ==Weitere Test-Daten-Provider== | ||
Zeile 131: | Zeile 175: | ||
Betrachten wir ein enum das Versandwege beschreibt: | Betrachten wir ein enum das Versandwege beschreibt: | ||
{{java|code= | {{java|code= | ||
enum Versandweg { | |||
E_MAIL, | |||
SMS, | |||
POST | |||
} | |||
}} | }} | ||
und die zu testende Methode die eine TAN über einen Versandweg verschickt: | und die zu testende Methode die eine TAN über einen Versandweg verschickt: | ||
{{java|code= | {{java|code= | ||
public class Versender { | |||
Response sendTan(Versandweg weg) { | Response sendTan(Versandweg weg) { | ||
... | ... | ||
} | } | ||
} | |||
}} | }} | ||
===Alle Ausprägungen testen=== | ===Alle Ausprägungen testen=== | ||
Zeile 149: | Zeile 193: | ||
gleiche Ergebnis erwarten: | gleiche Ergebnis erwarten: | ||
{{java|code= | {{java|code= | ||
@ParameterizedTest | |||
@EnumSource(value=Versandweg.class) | |||
void verwandWegLiefertOk(Versandweg weg) { | |||
Assertions.assertThat(new Versender().sendTan(weg)).isEqualTo(Response.OK); | Assertions.assertThat(new Versender().sendTan(weg)).isEqualTo(Response.OK); | ||
} | |||
}} | }} | ||
Die erste Annotation weist die Methode als parametrisierten Test aus. Die zweite Annotation gibt an, welches enum | Die erste Annotation weist die Methode als parametrisierten Test aus. Die zweite Annotation gibt an, welches enum | ||
Zeile 189: | Zeile 233: | ||
jeweils einzelne Test-Konstellationen beschreiben. Damit lassen sich parametrisierte Tests definieren, die alle oder ausgewählte Konstellationen testen: | jeweils einzelne Test-Konstellationen beschreiben. Damit lassen sich parametrisierte Tests definieren, die alle oder ausgewählte Konstellationen testen: | ||
{{java|code= | {{java|code= | ||
@ParameterizedTest | |||
@EnumSource(value = Kunde.class, names = { "MIT_KDNR", "MIT_IHNR", "MIT_KDNR_UND_IHNR" }) | |||
void darfElektronischeDokumentEinstellungSehen(Kunde kunde) { | |||
assertThat(verwalter(kunde).darfElectronicDocumentsSehen()).isTrue(); | assertThat(verwalter(kunde).darfElectronicDocumentsSehen()).isTrue(); | ||
} | |||
}} | }} | ||
In diesem Beispiel wurde Kunden konfiguriert die ein Kunden- oder Inhabernummer oder beides haben. Jeder der | In diesem Beispiel wurde Kunden konfiguriert die ein Kunden- oder Inhabernummer oder beides haben. Jeder der | ||
Kunden muß seine elektronischen Dokumente sehen dürfen. | Kunden muß seine elektronischen Dokumente sehen dürfen. | ||
== Test-Beschreibung == | |||
Parameterisierte Tests unterscheiden sich zunächst nur in ihren Argumenten. Wenn JUnit die Tests ausführt, | |||
wird aus den Argumenten eine beschreibung generiert die nicht immer leicht zu entziffern ist. | |||
Alternativ kann man der Annotation einen String mitgeben aus dem der Name generiert wird: | |||
{{java|code= | |||
@ParameterizedTest(name = "Test") | |||
}} | |||
Das Beispiel ist extrem unnütz, denn jetzt haben alle Einzel-Tests die gleiche Beschreibung -- "Test". | |||
Um daraus eine brauchbare Beschreibung zu machen bietet JUnit5 Textersatz an: | |||
Verwendet man {{java|{index}}} in der Beschreibung, wird die Nummer des Tests angezeigt. Sehr praktisch, wenn man den Testfall später sucht. | |||
Die übergebenen Parameter kann man mit {{java|{x}}} anzeigen, wobei {{java|x}} für die Positions-Nummer eines Parameters steht -- beginnend mit 0. |
Aktuelle Version vom 19. November 2024, 19:39 Uhr
Ein einzelner Unit-Tests befaßt sich mit einem einzelnen Test-Fall und hat im Regelfall eine einzelne Assertion. Dieses einfache Beispiel testet die korrekte Konvertierung von Datum-Strings in ein Date-Objekt. Die drei Assertions prüfen hier zusammen, ob das Datum korrekt ist:
@Test public void toDateLiefertKorrektesDatum() { Date convertedDate = Convert.toDate("01.5.1985"); assertThat(convertedDate).hasDayOfMonth(1); assertThat(convertedDate).hasMonth(5); assertThat(convertedDate).hasYear(1985); }
Nun soll er Konverter mit unterschiedlichen Datums-Angaben umgehen: Mit und ohne führende Nullen, mit und ohne Jahrhundert, vielleicht soll auch ein Minuszeichen anstelle des Punkts erlaubt sein. Für jeden Testfall kann man nun die Test-Methode kopieren und entsprechend anpassen. Die Folgen sind klar: jede Menge Test-Code, sinkende Übersichtlichkeit und für jede neue Test-Methode braucht es einen neuen Namen. Das DRY-Prinzip soll auch für Test-Code gelten.
Eine wesentliche Bereicherung von JUnit-5 ist die Unterstützung von Parameter-gesteuerten Test-Methoden die weit über das hinausgeht was JUnit-4 oder Test-NG zu bieten hatten. Eine Vielzahl von Test-Situationen läßt sich damit abdecken und vor allem eindampfen.
Eine Liste einzelner Parameter
Betrachten wir –- angelehnt an das einleitende Beispiel -– die einfachste Situation: Einen Testfall soll mit einer Reihe einzelner Strings aufgerufen werden. Dazu parametrisieren wir zunächst die Test-Methode und ziehen den Datum-String als Parameter heraus:
void toDateLiefertKorrektesDatum(String datum) { Date convertedDate = Convert.toDate(datum); assertThat(convertedDate).hasDayOfMonth(1); assertThat(convertedDate).hasMonth(5); assertThat(convertedDate).hasYear(1985); }
Dann legen wir die Datum-Strings fest mit denen getestet werden soll:
"1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985"
.
Um die neue Test-Methode mit den Test-Fälle zu verknüpfen, verwenden wir JUnit5-Annotationen und setzen sie
über unsere parametrisierte Test-Methode:
@ParameterizedTest @ValueSource(strings = { "1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985" })
Die erste Annotation ersetzt die @Test
-Annotation und macht die Methode zum (parametrisierten) Test.
Die zweite Annotation legt die Quelle der zu verwendenden Werte fest.
In diesem Falle ist dies eine Liste von Strings. Der Test wird bei der
Ausführung für jeden String einmal aufgerufen, wobei der jeweilige String als Argument übergeben wird.
Anstelle von Strings sind hier auch alle numerischen, primitiven Typen int, long, float und einiges mehr erlaubt.
Die Anntotations-Parameter kann man in Eclipse mit Code-Assist erkunden oder in der JUnit5-Doku nachlesen.
Beachtenswert ist, daß Annotationen nur Konstanten als Argumente übernehmen, also Ausdrücke die zur Compile-Zeit ausgewertet werden können. Objekte zu übergeben ist so nicht möglich.
Wie man damit umgeht, sehen wir in den folgenden Abschnitten.
Vorher aber noch ein Wort zu null
.
Was ist mit null?
Die eben vorgestellte Werte-Liste hat einen kleinen Haken: Es ist nicht möglich, den Wert null als Argument zu
verwenden. Um einen Testfall für null zu erzeugen, kann man nun freilich eine neue Test-Methode erstellen und
null separat in diesem Test prüfen. Man kann dem Test aber auch zusätzlich die Annotation @NullSource
hinzufügen. JUnit5 erzeugt dann einen zusätzlichen Testfall für null
:
@ParameterizedTest @NullSource @ValueSource(strings = { "1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985" }) public void toDateLiefertKorrektesDatum(String datum) { // hier kommt der Test-Code hin... }
Unterstützt wird @NullSource
ab Version 5.4.
Einzelne Argumente aus einer Methode
Anstelle der Angabe von Annotations-Parametern kann man eine Methode schreiben die die Argumente liefert.
Die Methode muß static
sein und einen Stream
von "irgendwas" liefern.
Das obige Beispiel kann man dann so schreiben:
@ParameterizedTest @MethodSource("testDatenMethode") void toDateLiefertKorrektesDatum(String datum) { // sieht genauso aus wie oben } static Stream<String> testDatenMethode() { return Stream.of("1.5.1985", "01.5.1985", "1.05.1985", "01.05.1985");
Das Beispiel hat den gleichen Effekt wie die Implementierung im vorabgegangenen Abschnitt, aber die Möglichkeiten sind offensichtlich viel weiterreichend. In der Test-Daten-Methode lassen sich beliebige Objete liefern, die bei bedarf auch dynamisch erzeugt werden können. Der Wert {{java|null} ist hier kein Problem, er darf im Stream enthalten sein.
Hat die Testdaten-Methode den gleichen Namen wie die parameterisierte Test-Methode,
kann man den Parameter der Annotation @MethodSource
übrigens weglassen.
Mehrere Parameter für die Test-Methode
Für viele Unit-Tests reicht die Extraktion eines einzelnen Parameters nicht aus. Im folgenden Beispiel wird die Qualität von Passworten getestet. Das Wort "Hallo" zum Beispiel wird abgewiesen, weil es nur Buchstaben enthält:
public void passwortWirdNichtAkzeptiert() { assertThat(policy.validate("Hallo")).as("nur Buchstaben").isFalse(); }
Wofür die Parameter in der Test-Methode verwendet werden, spielen keine Rolle, es geht nur darum, den Test mehrfach aufzurufen und dabei mehrere Parameter zu übergeben. Im vorliegenden Falle möchten wir unterschiedliche Passwort-Kandidaten testen und dabei jeden Test mit einer Beschreibung versehen, der Auskunft gibt warum das Wort nicht als Passwort akzeptiert wird. Die Test-Methode sieht nun so aus:
public void passwortWirdNichtAkzeptiert(String passwort, String grund) { assertThat(policy.validate(passwort)).as(grund).isFalse(); }
Für die Erzeugung der Test-Daten schreiben wir diesmal eine weitere Methode. Sie erzeugt einen Stream von Arguments-Objekten von denen jedes die Daten für einen Test-Aufruf enthält:
static Stream<Arguments> pwdWirdNichtAkzeptiert() { return Stream.of( Arguments.of("Hallo", "nur Buchstaben"), Arguments.of("1234567", "nur Ziffern"), Arguments.of("!%#&/()", "nur Sonderzeichen") ); }
Die Methode für die Testdaten-Erzeugung muß folgende Eigenschaften haben:
- sie muß static deklariert sein
- sie muß zur Test-Klasse gehören
- sie darf keinen Parameter haben und muß einen Stream von Arguments-Objekten liefern
- sie darf in beliebiger Sichtbarkeit deklariert werden
Die Methode darf zwar private deklariert werden, Java wird sich aber darüber beschweren, daß die Methode nirgends verwendet wird. Warum das so ist, erfahren wir weiter unten. Am einfachsten deklariert man sie daher package visible.
Die Klasse
org.junit.jupiter.params.provider.Arguments
ist ein Container, der eine Reihe von Werten beliebigen Typs (mit Ausnahme von λ-Ausdrücken) in fester
Reihenfolge aufnimmt. JUnit5 mappt die Werte der Arguments
-Objekte auf die Parameter der Test-Methode. Das
geschieht allerdings erst zur Laufzeit, der Entwickler ist daher selbst dafür verantwortlich die Typen zwischen
Testdaten-Provider und Test-Methode in Übereinstimmung zu halten.
Wir bringen nun Test-Methode und Testdaten-Provider-Methode zusammen. Das geschieht mithilfe folgender JUnit5-Annotationen:
@ParameterizedTest @MethodSource("pwdWirdNichtAkzeptiert")
Die erste Annotation kennen wir schon aus dem vorangegangenen Abschnitt. Die zweite Annotation weist die Test-Methode an die Testdaten aus der Methode "pwdWirdNichtAkzeptiert" zu beziehen. Der Name der Methode wird als String angegeben. Das bedeutet, daß der Entwickler für die korrekte Schreibweise verantwortlich ist, Fehler treten auch hier erst zur Laufzeit auf. Hat die Test-Daten-Methode den gleichen Namen wie die Test-Methode kann sie in der Annotation auch weggelassen werden. Die Annotation selbst hingegen kann nicht weggelassen werden. Da die Methode nur über den Namen referenziert wird, muß der Compiler tatsächlich annehmen, daß die Method nicht verwendet wird wenn sie als private deklariert wurde.
Lambda-Ausdrücke
Warum sollte man λ-Ausdrücke in einem parametrisierten Test verwenden? Denkt man etwa an ein Objekt mit einer Reihe von getter- und setter-Methoden, läßt sich mithilfe von λ-Ausdrücken die Objekt-Bestückung sehr kompakt vertesten.
ersetzt man in der oben vorgestellten Befüll-Methode ein String-Argument durch einen λ-Ausdruck läuft der compiler allerdings ganz flott auf einen Fehler, weil der λ-Ausdruck nicht in ein Objekt gecastet werden kann. Dazu braucht es eines (funktionalen) Interfaces. Man kann dazu die vom JDK bereitgestellten Interfaces nutzen oder eigene Interfaces definieren. Für Unit-Tests ist das zweitere vorzuziehen.
Stellen wir uns also mal eine POJO mit dem klassischen Getter und Setter vor:
public class Person { private String vorname;
Weitere Test-Daten-Provider
JUnit5 unterstützt noch eine Reihe weiterer Möglichkeiten, Testdaten für parametrisierte Tests zur Verfügung zu stellen:
- Java-enums
- CSV-Dateien
- Daten im CSV-Format
- separate Klassen
Enums
Eine besondere Unterstützung bei der Erstellung parametrisierter Tests erfahren die Java-enums. Grundsätzlich erfolgt die Parametrisierung wie dies hier am Beispiel von String-Argumenten beschrieben wurde. Auf die spezifischen Eigenschaften von enums und ihrem besonderen Wert für Unit-Tests wird hier eingegangen. Betrachten wir ein enum das Versandwege beschreibt:
enum Versandweg { E_MAIL, SMS, POST }
und die zu testende Methode die eine TAN über einen Versandweg verschickt:
public class Versender { Response sendTan(Versandweg weg) { ... } }
Alle Ausprägungen testen
Wir können nun für jeden Versandweg eine Test-Methode schreiben. Das geht auch einfacher, wenn wir immer das gleiche Ergebnis erwarten:
@ParameterizedTest @EnumSource(value=Versandweg.class) void verwandWegLiefertOk(Versandweg weg) { Assertions.assertThat(new Versender().sendTan(weg)).isEqualTo(Response.OK); }
Die erste Annotation weist die Methode als parametrisierten Test aus. Die zweite Annotation gibt an, welches enum für den Test zu verwenden ist. Die Test-Methode selbst muß ein Argument vom Typ des Enums akzeptieren. Die parametrisierte Version ist nicht nur kompakter. Wird das enum um weitere Ausprägungen ergänzt, werden automatisch Test dafür generiert. Ob das Ergebnis tatsächlich der gewünschten Anforderung entspricht kann JUnit natürlich nicht vorhersehen, aber die neue enum-Ausprägung wird zumindest betrachtet und läuft günstigenfalls auf einen Fehler.
Einzelne Ausprägungen testen
Möchte man nicht alle Ausprägungen testen sondern nur ganz bestimmte, kann man das der Annotation mitgeben. dafür gibt es zwei Modes: einschließend und ausschließend. Möchten wir etwa nur die Versandwege eMail und SMS testen, können wir die Annotation so schreiben (der ganze Rest bleibt gleich):
@EnumSource(value = Versandweg.class, mode = Mode.INCLUDE, names = { "E_MAIL", "SMS" })
Der Parameter mod
gibt an, daß nur die angegebenen Ausprägungen getestet werden sollen. Da "Include" der
default ist, kann de Mo in diesem Falle auch weggelassen werden. Der Parameter names
ist eine Liste von enum-Ausprägung-Namen die als Strings angegeben werden.
Man kann das vorgehen auch umdrehen un den Versandweg "POST" – als einzigen nicht-elektronischen Weg
ausschließen. Die Annotation sieht dann so aus:
@EnumSource(value = Versandweg.class, mode = Mode.EXCLUDE, names = { "POST" })
Der mode
-Parameter hat nun den Wert EXCLUDE
und darf natürlich nicht weggelassen werden. Der Parameter
names
ist wieder eine Liste von enum-Ausprägung-Namen die hier aus einem einzigen Wert besteht.
In beiden Fällen ist der Entwickler für die korrekte Schreibweise der Namen der enum-Ausprägungen
verantwortlich. Stimmt sie nicht überein, giebt's einen Laufzeitfehler bei der Test-Ausführung.
Während bei "INCLUDE" nur so viel Tests erzeugt werden, wie enum-Ausprägungen spezifiziert wurden, werden bei
"EXCLUDE" Tests für alle Ausprägungen erzeugt die nicht angegeben sind. Wird das enum um Ausprägungen
erweitert, dann werden im EXCLUDE-Falle neue Tests generiert, im INCLUDE-Falle nicht.
Pattern-Matching
Mit den beiden Modes MATCH_ALL und MATCH_ANY können die Namen von enum-Ausprägungen mit Hilfe von regulären Ausdrücken spezifiziert werden. Dieses Vorgehen ist jedoch problematisch und soll hier nicht weiter erörtert werden. Macht man bei Definition der Ausdrücke Fehler, kann es beispielsweise passieren, daß Ausprägungen ausgelassen werden und für diese dann keine Tests erzeugt werden. Das fällt nur auf wenn die TestAusführung manuell überprüft wird.
Test-Fälle als Enumerationen
bei der Verwendung von enum-Sources ist man nicht auf "produktive" enums beschränkt. Man kann auch Test-Situationen über enums definieren und diese enums dann verwenden um parametrisierte Tests zu füttern. Man kann zum Beispiel für die Test-Klasse enum "Kunde" definieren, dessen Ausprägungen jeweils einzelne Test-Konstellationen beschreiben. Damit lassen sich parametrisierte Tests definieren, die alle oder ausgewählte Konstellationen testen:
@ParameterizedTest @EnumSource(value = Kunde.class, names = { "MIT_KDNR", "MIT_IHNR", "MIT_KDNR_UND_IHNR" }) void darfElektronischeDokumentEinstellungSehen(Kunde kunde) { assertThat(verwalter(kunde).darfElectronicDocumentsSehen()).isTrue(); }
In diesem Beispiel wurde Kunden konfiguriert die ein Kunden- oder Inhabernummer oder beides haben. Jeder der Kunden muß seine elektronischen Dokumente sehen dürfen.
Test-Beschreibung
Parameterisierte Tests unterscheiden sich zunächst nur in ihren Argumenten. Wenn JUnit die Tests ausführt, wird aus den Argumenten eine beschreibung generiert die nicht immer leicht zu entziffern ist.
Alternativ kann man der Annotation einen String mitgeben aus dem der Name generiert wird:
@ParameterizedTest(name = "Test")
Das Beispiel ist extrem unnütz, denn jetzt haben alle Einzel-Tests die gleiche Beschreibung -- "Test". Um daraus eine brauchbare Beschreibung zu machen bietet JUnit5 Textersatz an:
Verwendet man {index
} in der Beschreibung, wird die Nummer des Tests angezeigt. Sehr praktisch, wenn man den Testfall später sucht.
Die übergebenen Parameter kann man mit {x
} anzeigen, wobei x
für die Positions-Nummer eines Parameters steht -- beginnend mit 0.