JUnit 5 Tutorial:Schreiben parametrisierter Tests
Dieser Blogbeitrag beschreibt, wie wir parametrisierte Tests mit JUnit 5 schreiben können. Nachdem wir diesen Blogbeitrag beendet haben, werden wir:
- Kann die erforderlichen Abhängigkeiten mit Maven und Gradle erhalten.
- Wissen, wie wir den Anzeigenamen jedes Methodenaufrufs anpassen können.
- Verstehen, wie wir verschiedene Argumentquellen verwenden können.
- Kann benutzerdefinierte Argumentkonverter schreiben.
Beginnen wir damit, die erforderlichen Abhängigkeiten abzurufen.
Erforderliche Abhängigkeiten abrufen
Bevor wir parametrisierte Tests mit JUnit 5 schreiben können, müssen wir sicherstellen, dass der junit-jupiter-params
Abhängigkeit wird aus dem Klassenpfad gefunden. Wenn wir den junit-jupiter
verwenden Aggregatorartefakt müssen wir nichts tun, da alle erforderlichen Abhängigkeiten bereits zum Klassenpfad hinzugefügt wurden. Andererseits, wenn wir nicht den junit-jupiter
verwenden Aggregator-Artefakt müssen wir einige Änderungen an unserem Build-Skript vornehmen.
Wenn wir Maven verwenden, müssen wir den junit-jupiter-params
hinzufügen Abhängigkeit von test
Umfang. Wir können dies tun, indem wir das folgende Snippet zu dependencies
hinzufügen Abschnitt unserer POM-Datei:
<dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter-params</artifactId> <version>5.8.2</version> <scope>test</scope> </dependency>
Wenn wir Gradle verwenden, müssen wir den junit-jupiter-params
hinzufügen Abhängigkeit zum testImplementation
Abhängigkeitskonfiguration. Wir können dies tun, indem wir das folgende Snippet zu unserem build.gradle hinzufügen Datei:
testImplementation( 'org.junit.jupiter:junit-jupiter-params:5.8.2' )
Fahren wir fort und schreiben unseren ersten parametrisierten Test mit JUnit 5.
Schreiben unserer ersten parametrisierten Tests
Wenn unsere Testmethode nur einen Methodenparameter verwendet, ist das entweder ein String
oder ein primitiver Typ, der von @ValueSource
unterstützt wird Anmerkung (byte
, char
, double
, float
, int
, long
, oder short
), können wir einen parametrisierten Test mit JUnit 5 schreiben, indem wir diesen Schritten folgen:
- Fügen Sie unserer Testklasse eine neue Testmethode hinzu und stellen Sie sicher, dass diese Methode einen
String
akzeptiert Objekt als Methodenparameter. - Konfigurieren Sie den Anzeigenamen der Testmethode.
- Kommentieren Sie die Testmethode mit dem
@ParameterizedTest
Anmerkung. Diese Anmerkung identifiziert parametrisierte Testmethoden. - Geben Sie die Methodenparameter an, die an unsere Testmethode übergeben werden. Weil unsere Testmethode einen
String
benötigt Objekt als Methodenparameter, können wir seine Methodenparameter bereitstellen, indem wir unsere Testmethode mit@ValueSource
kommentieren Anmerkung.
Nachdem wir unserer Testklasse einen neuen parametrisierten Test hinzugefügt haben, sieht sein Quellcode wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.assertNotNull; @DisplayName("Pass the method parameters provided by the @ValueSource annotation") class ValueSourceExampleTest { @DisplayName("Should pass a non-null message to our test method") @ParameterizedTest @ValueSource(strings = {"Hello", "World"}) void shouldPassNonNullMessageAsMethodParameter(String message) { assertNotNull(message); } }
Wenn wir unseren parametrisierten Test ausführen, sollten wir eine Ausgabe sehen, die wie folgt aussieht:
Pass the method parameters provided by the @ValueSource annotation |_ Should pass a non-null message to our test method |_ [1] Hello |_ [2] World
Obwohl diese Ausgabe ziemlich sauber aussieht, möchten wir manchmal unseren eigenen Anzeigenamen für jeden Methodenaufruf bereitstellen. Lassen Sie uns herausfinden, wie wir es tun können.
Anpassen des Anzeigenamens jedes Methodenaufrufs
Wir können den Anzeigenamen jedes Methodenaufrufs anpassen, indem wir den Wert von @ParameterizedTest
festlegen name
der Anmerkung Attribut. Dieses Attribut unterstützt die folgenden Platzhalter:
{displayName}
:Der Anzeigename der Testmethode.{index}
:Der Index des aktuellen Aufrufs. Beachten Sie, dass der Index des ersten Aufrufs eins ist.{arguments}
:Eine durch Kommas getrennte Liste, die alle an die Testmethode übergebenen Argumente enthält.{argumentsWithNames}
:Eine durch Kommas getrennte Liste, die alle Argumente (einschließlich des Namens des Methodenparameters) enthält, die an die Testmethode übergeben wurden.{i}
:Der eigentliche Methodenparameter (i
gibt den Index des Methodenparameters an). Beachten Sie, dass der Index des ersten Methodenparameters Null ist.
Geben wir unserer Testmethode einen benutzerdefinierten Anzeigenamen. Dieser Anzeigename muss den Index des aktuellen Aufrufs und den bereitgestellten Methodenparameter anzeigen. Nachdem wir den benutzerdefinierten Anzeigenamen für jeden Methodenaufruf konfiguriert haben, sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; import static org.junit.jupiter.api.Assertions.assertNotNull; @DisplayName("Pass the method parameters provided by the @ValueSource annotation") class ValueSourceExampleTest { @DisplayName("Should pass a non-null message to our test method") @ParameterizedTest(name = "{index} => message=''{0}''") @ValueSource(strings = {"Hello", "World"}) void shouldPassNonNullMessageAsMethodParameter(String message) { assertNotNull(message); } }
Wenn wir unseren parametrisierten Test ausführen, sollten wir eine Ausgabe sehen, die wie folgt aussieht:
Pass the method parameters provided by the @ValueSource annotation |_ Should pass a non-null message to our test method |_ 1 => message='Hello' |_ 2 => message='World'
Wie wir uns erinnern, der @ValueSource
Anmerkung ist eine gute Wahl, wenn unsere Testmethode nur einen Methodenparameter verwendet, der von @ValueSource
unterstützt wird Anmerkung. Meistens ist dies jedoch nicht der Fall. Als nächstes werden wir herausfinden, wie wir dieses Problem lösen können, indem wir verschiedene Argumentquellen verwenden.
Argumentquellen verwenden
Der @ValueSource
Annotation ist die einfachste Argumentquelle, die von JUnit 5 unterstützt wird. JUnit 5 unterstützt jedoch auch andere Argumentquellen. Alle unterstützten Argumentquellen werden mithilfe von Anmerkungen aus org.junit.jupiter.params.provider
konfiguriert Paket.
Dieser Abschnitt beschreibt, wie wir die komplexeren Argumentquellen verwenden können, die von JUnit 5 bereitgestellt werden. Beginnen wir damit, herauszufinden, wie wir enum
übergeben können Werte zu unserem parametrisierten Test.
Aufzählungswerte an unseren parametrisierten Test übergeben
Wenn unser parametrisierter Test ein enum
dauert value als Methodenparameter müssen wir unsere Testmethode mit dem @EnumSource
annotieren Anmerkung und spezifizieren Sie die Enum-Werte, die an unsere Testmethode übergeben werden.
Nehmen wir an, wir müssen einen parametrisierten Test schreiben, der einen Wert von Pet
annimmt enum
als Methodenparameter. Der Quellcode von Pet
enum sieht folgendermaßen aus:
enum Pet { CAT, DOG; }
Wenn wir alle enum
weitergeben wollen Werte zu unserer Testmethode, müssen wir unsere Testmethode mit dem @EnumSource
annotieren Anmerkung und geben Sie den enum
an deren Werte an unsere Testmethode übergeben werden. Nachdem wir dies getan haben, sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import static org.junit.jupiter.api.Assertions.assertNotNull; @DisplayName("Pass enum values to our test method") class EnumSourceExampleTest { @DisplayName("Should pass non-null enum values as method parameters") @ParameterizedTest(name = "{index} => pet=''{0}''") @EnumSource(Pet.class) void shouldPassNonNullEnumValuesAsMethodParameter(Pet pet) { assertNotNull(pet); } }
Wenn wir diese Testmethode ausführen, sehen wir, dass JUnit 5 alle Werte von Pet
übergibt enum
zu unserer Testmethode:
Pass enum values to our test method |_ Should pass non-null enum values as method parameters |_ 1 => pet='CAT' |_ 2 => pet='DOG'
Wenn wir den enum
angeben möchten Werte, die an unsere Testmethode übergeben werden, können wir den enum
angeben Werte, indem Sie den Wert von @EnumSource
festlegen names
der Anmerkung Attribut. Stellen wir sicher, dass der Wert:Pet.CAT
wird unserem Testverfahren übergeben.
Nachdem wir den verwendeten Enum-Wert angegeben haben, sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.EnumSource; import static org.junit.jupiter.api.Assertions.assertNotNull; @DisplayName("Pass enum values to our test method") class EnumSourceExampleTest { @DisplayName("Should pass only the specified enum value as a method parameter") @ParameterizedTest(name = "{index} => pet=''{0}''") @EnumSource(value = Pet.class, names = {"CAT"}) void shouldPassNonNullEnumValueAsMethodParameter(Pet pet) { assertNotNull(pet); } }
Wenn wir diese Testmethode ausführen, sehen wir, dass JUnit 5 nur den Wert übergibt:Pet.CAT
zu unserer Testmethode:
Pass enum values to our test method |_ Should pass non-null enum values as method parameters |_ 1 => pet='CAT'
Wir haben jetzt gelernt, wie wir zwei verschiedene Argumentquellen verwenden können, die es uns ermöglichen, einen Methodenparameter an unsere Testmethode zu übergeben. Meistens möchten wir jedoch mehrere Methodenparameter an unseren parametrisierten Test übergeben. Als nächstes werden wir herausfinden, wie wir dieses Problem lösen können, indem wir das CSV-Format verwenden.
Erstellen unserer Testdaten mithilfe des CSV-Formats
Wenn wir mehrere Argumente an die aufgerufene Testmethode übergeben müssen und die bereitgestellten Testdaten nur von einer Testmethode (oder wenigen Testmethoden) verwendet werden, können wir unsere Testdaten mit dem @CsvSource
konfigurieren Anmerkung. Wenn wir diese Anmerkung zu einer Testmethode hinzufügen, müssen wir die Testdaten konfigurieren, indem wir ein Array von String
verwenden Objekte. Wenn wir unsere Testdaten spezifizieren, müssen wir diese Regeln befolgen:
- Eine
String
Objekt muss alle Argumente eines Methodenaufrufs enthalten. - Die unterschiedlichen Argumentwerte müssen durch ein Komma getrennt werden.
- Die in jeder Zeile gefundenen Argumentwerte müssen dieselbe Reihenfolge wie die Methodenparameter unserer Testmethode verwenden.
Lassen Sie uns die Argumente konfigurieren, die an sum()
übergeben werden Methode. Diese Methode benötigt drei Methodenparameter:Die ersten beiden Methodenparameter enthalten zwei int
Werte und der dritte Methodenparameter gibt die erwartete Summe der bereitgestellten int
an Werte.
Nachdem wir die Testdaten unseres parametrisierten Tests konfiguriert haben, sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; @DisplayName("Should pass the method parameters provided by the @CsvSource annotation") class CsvSourceExampleTest { @DisplayName("Should calculate the correct sum") @ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}") @CsvSource({ "1, 1, 2", "2, 3, 5" }) void sum(int a, int b, int sum) { assertEquals(sum, a + b); } }
Auch wenn das ziemlich sauber aussieht, haben wir manchmal so viele Testdaten, dass es keinen Sinn macht, sie zu unserer Testklasse hinzuzufügen, weil unsere Testklasse unlesbar werden würde. Lassen Sie uns herausfinden, wie wir die Testdaten laden können, die an sum()
übergeben werden Methode aus einer CSV-Datei.
Laden unserer Testdaten aus einer CSV-Datei
Wir können unsere Testdaten aus einer CSV-Datei laden, indem Sie diesen Schritten folgen:
Zuerst , müssen wir eine CSV-Datei erstellen, die unsere Testdaten enthält, und diese Datei in den Klassenpfad einfügen. Wenn wir unsere Testdaten zur erstellten CSV-Datei hinzufügen, müssen wir diese Regeln befolgen:
- Eine Zeile muss alle Argumente eines Methodenaufrufs enthalten.
- Die unterschiedlichen Argumentwerte müssen durch ein Komma getrennt werden.
- Die in jeder Zeile gefundenen Argumentwerte müssen dieselbe Reihenfolge wie die Methodenparameter unserer Testmethode verwenden.
Die test-data.csv Datei konfiguriert die Testdaten, die an sum()
übergeben werden Methode. Diese Datei finden Sie in src/test/resources Verzeichnis, und sein Inhalt sieht wie folgt aus:
1,1,2 2,3,5 3,5,8
Zweiter , müssen wir unsere Testmethode mit dem @CsvFileSource
annotieren Anmerkung und konfigurieren Sie den Speicherort unserer CSV-Datei. Nachdem wir dies getan haben, sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvFileSource; import static org.junit.jupiter.api.Assertions.assertEquals; @DisplayName("Should pass the method parameters provided by the test-data.csv file") class CsvFileSourceExampleTest { @DisplayName("Should calculate the correct sum") @ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}") @CsvFileSource(resources = "/test-data.csv") void sum(int a, int b, int sum) { assertEquals(sum, a + b); } }
Wir können jetzt mehrere Methodenparameter an unseren parametrisierten Test übergeben. Der Haken an der Sache ist jedoch, dass die Methodenparameter unserer parametrisierten Tests vom DefaultArgumentConverter
unterstützt werden müssen Klasse. Sein Javadoc besagt, dass:
Der DefaultArgumentConverter kann Zeichenfolgen in eine Reihe von primitiven Typen und ihre entsprechenden Wrapper-Typen (Byte, Short, Integer, Long, Float und Double), Datums- und Zeittypen aus dem java.time-Paket und einige zusätzliche gängige Java-Typen konvertieren Typen wie File, BigDecimal, BigInteger, Currency, Locale, URI, URL, UUID usw.
Als nächstes werden wir herausfinden, wie wir dieses Problem lösen können, indem wir eine Factory-Methode und einen benutzerdefinierten ArgumentsProvider
verwenden .
Erstellen unserer Testdaten mit einer Fabrikmethode
Wenn alle parametrisierten Tests, die die erstellten Testdaten verwenden, aus derselben Testklasse stammen und die Logik, die die Testdaten erstellt, nicht „zu komplex“ ist, sollten wir unsere Testdaten mithilfe einer Factory-Methode erstellen.
Wenn wir diesen Ansatz verwenden möchten, müssen wir einen static
hinzufügen factory-Methode in unsere Testklasse und implementieren Sie diese Methode, indem Sie diese Regeln befolgen:
- Die Factory-Methode darf keine Methodenparameter übernehmen.
- Die Factory-Methode muss einen
Stream
zurückgeben ,Iterable
,Iterator
, oder ein Array vonArguments
Objekte. Das von unserer Factory-Methode zurückgegebene Objekt enthält die Argumente aller Testmethodenaufrufe. - Ein
Arguments
Objekt muss alle Argumente eines einzelnen Testmethodenaufrufs enthalten. - Wir können einen neuen
Arguments
erstellen Objekt durch Aufrufen desstatic of()
Methode desArguments
Schnittstelle. Die fürof()
bereitgestellten Argumente -Methode werden an unsere Testmethode übergeben, wenn sie von JUnit 5 aufgerufen wird. Deshalb müssen die bereitgestellten Argumente dieselbe Reihenfolge wie die Methodenparameter unserer Testmethode verwenden.
Lassen Sie uns diese Regeln demonstrieren, indem wir eine Factory-Methode implementieren, die die Testdaten erstellt, die an sum()
übergeben werden Methode (wir haben diese Methode bereits in den vorherigen Beispielen verwendet). Nachdem wir diese Factory-Methode implementiert haben, sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @DisplayName("Should pass the method parameters provided by the sumProvider() method") class MethodSourceExampleTest { @DisplayName("Should calculate the correct sum") @ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}") void sum(int a, int b, int sum) { assertEquals(sum, a + b); } private static Stream<Arguments> sumProvider() { return Stream.of( Arguments.of(1, 1, 2), Arguments.of(2, 3, 5) ); } }
Nachdem wir diese Methode implementiert haben, müssen wir sicherstellen, dass ihr Rückgabewert verwendet wird, wenn JUnit 5 unsere parametrisierte Testmethode ausführt. Wir können dies tun, indem wir diesen Schritten folgen:
- Kommentieren Sie unsere Testmethode mit dem
@MethodSource
Anmerkung. - Konfigurieren Sie den Namen der Factory-Methode, die unsere Testdaten erstellt.
Nachdem wir die erforderlichen Änderungen an unserer Testklasse vorgenommen haben, sieht ihr Quellcode wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @DisplayName("Should pass the method parameters provided by the sumProvider() method") class MethodSourceExampleTest { @DisplayName("Should calculate the correct sum") @ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}") @MethodSource("sumProvider") void sum(int a, int b, int sum) { assertEquals(sum, a + b); } private static Stream<Arguments> sumProvider() { return Stream.of( Arguments.of(1, 1, 2), Arguments.of(2, 3, 5) ); } }
Dieser Ansatz funktioniert relativ gut, solange die Fabrikmethode einfach ist und alle Testmethoden, die die Fabrikmethode verwenden, aus derselben Testklasse gefunden werden. Wenn eine dieser Bedingungen false
ist , müssen wir einen benutzerdefinierten ArgumentsProvider
implementieren .
Erstellen unserer Testdaten durch Verwendung eines benutzerdefinierten ArgumentsProviders
Wenn die Testmethoden, die unsere Testdaten verwenden, aus verschiedenen Testklassen gefunden werden oder die Logik, die die erforderlichen Testdaten erstellt, so komplex ist, dass wir sie nicht zu unserer Testklasse hinzufügen möchten, müssen wir einen benutzerdefinierten ArgumentsProvider
.
Wir können dies tun, indem wir eine Klasse erstellen, die den ArgumentsProvider
implementiert Schnittstelle. Nachdem wir diese Klasse erstellt haben, müssen wir den provideArguments()
implementieren Methode, die einen Stream
zurückgibt von Arguments
Objekte. Wenn wir den zurückgegebenen Stream
erstellen Objekt, müssen wir diese Regeln befolgen:
- Das zurückgegebene Objekt muss die Argumente aller Testmethodenaufrufe enthalten.
- Ein
Arguments
Objekt muss alle Argumente eines einzelnen Testmethodenaufrufs enthalten. - Wir können einen neuen
Arguments
erstellen Objekt durch Aufrufen vonstatic of()
Methode desArguments
Schnittstelle. Die fürof()
bereitgestellten Argumente -Methode werden an unsere Testmethode übergeben, wenn sie von JUnit 5 aufgerufen wird. Deshalb müssen die bereitgestellten Argumente dieselbe Reihenfolge wie die Methodenparameter unserer Testmethode verwenden.
Lassen Sie uns einen benutzerdefinierten ArgumentsProvider
erstellen die die Testdaten bereitstellt, die an sum()
übergeben werden Methode. Wir können dies tun, indem wir diesen Schritten folgen:
Zuerst , haben wir einen benutzerdefinierten ArgumentsProvider
geschrieben Klasse, die die Testdaten zurückgibt, die an sum()
übergeben werden Methode.
Nachdem wir einen benutzerdefinierten ArgumentsProvider
erstellt haben Klasse sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @DisplayName("Should pass the method parameters provided by the CustomArgumentProvider class") class ArgumentsSourceExampleTest { @DisplayName("Should calculate the correct sum") @ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}") void sum(int a, int b, int sum) { assertEquals(sum, a + b); } static class CustomArgumentProvider implements ArgumentsProvider { @Override public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception { return Stream.of( Arguments.of(1, 1, 2), Arguments.of(2, 3, 5) ); } } }
Zweiter , müssen wir den verwendeten ArgumentsProvider
konfigurieren indem Sie unsere Testmethode mit dem @ArgumentsSource
kommentieren Anmerkung. Nachdem wir dies getan haben, sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.extension.ExtensionContext; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.ArgumentsProvider; import org.junit.jupiter.params.provider.ArgumentsSource; import java.util.stream.Stream; import static org.junit.jupiter.api.Assertions.assertEquals; @DisplayName("Should pass the method parameters provided by the CustomArgumentProvider class") class ArgumentsSourceExampleTest { @DisplayName("Should calculate the correct sum") @ParameterizedTest(name = "{index} => a={0}, b={1}, sum={2}") @ArgumentsSource(CustomArgumentProvider.class) void sum(int a, int b, int sum) { assertEquals(sum, a + b); } static class CustomArgumentProvider implements ArgumentsProvider { @Override public Stream<? extends Arguments> provideArguments(ExtensionContext context) throws Exception { return Stream.of( Arguments.of(1, 1, 2), Arguments.of(2, 3, 5) ); } } }
Wir können unsere Testdaten jetzt mithilfe von Factory-Methoden und benutzerdefiniertem ArgumentsProvider
erstellen Klassen. Auch wenn uns diese Methoden erlauben, die Beschränkungen von DefaultArgumentConverter
zu ignorieren Klasse, manchmal möchten wir unsere Testdaten mithilfe von Zeichenfolgen bereitstellen, da uns dies hilft, Tests zu schreiben, die einfacher zu lesen sind als Tests, die Factory-Methoden oder benutzerdefinierte ArgumentsProvider
verwenden Klassen.
Als nächstes werden wir herausfinden, wie wir dieses Problem lösen können, indem wir einen benutzerdefinierten ArgumentConverter
verwenden .
Einen benutzerdefinierten Argumentkonverter verwenden
Ein ArgumentConverter
hat nur eine Aufgabe:Es konvertiert das Quellobjekt in eine Instanz eines anderen Typs. Wenn die Konvertierung fehlschlägt, muss ein ArgumentConversionException
ausgelöst werden .
Lassen Sie uns einen ArgumentConverter
erstellen das kann einen String
umwandeln Objekt in einen Message
Objekt. Die Message
class ist eine einfache Wrapper-Klasse, die einfach die als Konstruktor-Argument angegebene Nachricht umschließt. Sein Quellcode sieht wie folgt aus:
final class Message { private final String message; Message(String message) { this.message = message; } String getMessage() { return message; } }
Wir können unseren benutzerdefinierten ArgumentConverter
erstellen indem Sie diesen Schritten folgen:
Zuerst , müssen wir eine Klasse namens MessageConverter
erstellen die den ArgumentConverter
implementiert Schnittstelle. Nachdem wir diese Klasse erstellt haben, sieht ihr Quellcode wie folgt aus:
import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.params.converter.ArgumentConversionException; import org.junit.jupiter.params.converter.ArgumentConverter; final class MessageConverter implements ArgumentConverter { @Override public Object convert(Object source, ParameterContext context) throws ArgumentConversionException { } }
Zweiter , müssen wir den convert()
implementieren Methode, indem Sie diesen Schritten folgen:
- Werfen Sie einen neuen
ArgumentConversionException
wenn das Quellobjekt nicht gültig ist. Das Quellobjekt muss einString
sein das ist nichtnull
oder leer. - Erstellen Sie einen neuen
Message
Objekt und gibt das erstellte Objekt zurück.
Nachdem wir den convert()
implementiert haben Methode, der Quellcode von MessageConverter
Klasse sieht wie folgt aus:
import org.junit.jupiter.api.extension.ParameterContext; import org.junit.jupiter.params.converter.ArgumentConversionException; import org.junit.jupiter.params.converter.ArgumentConverter; final class MessageConverter implements ArgumentConverter { @Override public Object convert(Object source, ParameterContext context) throws ArgumentConversionException { checkSource(source); String sourceString = (String) source; return new Message(sourceString); } private void checkSource(Object source) { if (source == null) { throw new ArgumentConversionException("Cannot convert null source object"); } if (!source.getClass().equals(String.class)) { throw new ArgumentConversionException( "Cannot convert source object because it's not a string" ); } String sourceString = (String) source; if (sourceString.trim().isEmpty()) { throw new ArgumentConversionException( "Cannot convert an empty source string" ); } } }
Nachdem wir unseren benutzerdefinierten ArgumentConverter
erstellt haben , müssen wir einen parametrisierten Test erstellen, der unseren benutzerdefinierten ArgumentConverter
verwendet . Wir können diesen Test erstellen, indem wir diesen Schritten folgen:
Zuerst , müssen wir eine neue parametrisierte Testmethode erstellen, indem wir diesen Schritten folgen:
- Fügen Sie unserer Testklasse eine neue parametrisierte Testmethode hinzu und stellen Sie sicher, dass die Methode zwei
Message
akzeptiert Objekte als Methodenparameter. - Kommentieren Sie die Testmethode mit dem
@CsvSource
Anmerkung und konfigurieren Sie die Testdaten mithilfe des CSV-Formats. - Vergewissern Sie sich, dass
Message
Objekte, die als Methodenparameter angegeben werden, enthalten dieselbe Nachricht.
Nachdem wir unsere Testmethode erstellt haben, sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; @DisplayName("Pass converted Message objects to our test method") class MessageConverterExampleTest { @DisplayName("Should pass same messages as method parameters") @ParameterizedTest(name = "{index} => actual={0}, expected={1}") @CsvSource({ "Hello, Hello", "Hi, Hi", }) void shouldPassMessages(Message actual, Message expected) { assertEquals(expected.getMessage(), actual.getMessage()); } }
Zweiter , müssen wir den ArgumentConverter
konfigurieren die die an unsere Testmethode übergebenen Argumente erstellt. Wir können dies tun, indem wir die Methodenparameter mit dem @ConvertWith
annotieren Anmerkung. Dabei müssen wir den verwendeten ArgumentConverter
konfigurieren indem Sie den Wert von @ConvertWith
setzen value
der Anmerkung Attribut.
Nachdem wir den verwendeten ArgumentConverter
konfiguriert haben , sieht der Quellcode unserer Testklasse wie folgt aus:
import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.converter.ConvertWith; import org.junit.jupiter.params.provider.CsvSource; import static org.junit.jupiter.api.Assertions.assertEquals; @DisplayName("Pass converted Message objects to our test method") class MessageConverterExampleTest { @DisplayName("Should pass same messages as method parameters") @ParameterizedTest(name = "{index} => actual={0}, expected={1}") @CsvSource({ "Hello, Hello", "Hi, Hi", }) void shouldPassMessages(@ConvertWith(MessageConverter.class) Message actual, @ConvertWith(MessageConverter.class) Message expected) { assertEquals(expected.getMessage(), actual.getMessage()); } }
Wir können jetzt parametrisierte Tests mit JUnit 5 schreiben. Fassen wir zusammen, was wir aus diesem Blogbeitrag gelernt haben.
Zusammenfassung
Dieser Blogbeitrag hat uns sieben Dinge beigebracht:
- Bevor wir parametrisierte Tests mit JUnit 5 schreiben können, müssen wir sicherstellen, dass der
junit-jupiter-params
Abhängigkeit wird aus dem Klassenpfad gefunden. - Wir müssen unsere parametrisierte Testmethode mit dem
@ParameterizedTest
annotieren Anmerkung. - Wir können den Anzeigenamen jedes Methodenaufrufs anpassen, indem wir den Wert von
@ParameterizedTest
festlegenname
der Anmerkung Attribut. - Wenn wir unsere Testdaten konfigurieren, müssen unsere Testdaten dieselbe Reihenfolge wie die Methodenparameter unserer Testmethode verwenden.
- Wenn wir "komplexe" Objekte an parametrisierte Tests übergeben wollen, die aus derselben Testklasse gefunden werden, und die Logik, die diese Argumente erstellt, nicht "zu komplex" ist, sollten wir diese Argumente mithilfe einer Factory-Methode erstellen. li>
- Wenn die Testmethoden, die unsere Testdaten verwenden, aus verschiedenen Testklassen stammen oder die Logik, die die erforderlichen Testdaten erstellt, so komplex ist, dass wir sie nicht zu unserer Testklasse hinzufügen möchten, müssen wir eine benutzerdefinierte erstellen
ArgumentsProvider
. - Wenn wir unsere Testdaten mithilfe von Strings bereitstellen und Methodenparameter verwenden möchten, die von den Standardargumentkonvertern nicht unterstützt werden, müssen wir einen benutzerdefinierten
ArgumentConverter
implementieren .