1. Übersicht
Vor der Einführung von JUnit 5 müsste das JUnit-Team es mit der Core-API tun, um eine coole neue Funktion einzuführen. Mit JUnit 5 entschied das Team, es sei an der Zeit, die Erweiterung der JUnit-API außerhalb von JUnit selbst, einer Kernphilosophie von JUnit 5, mit dem Namen „https://github.com/junit-team/junit5/wiki/Core-Principles“ voranzutreiben[Erweiterungspunkte gegenüber Funktionen bevorzugen]“.
In diesem Artikel konzentrieren wir uns auf eine dieser Erweiterungspunktschnittstellen - ParameterResolver -, die Sie zum Einfügen von Parametern in Ihre Testmethoden verwenden können. Es gibt verschiedene Möglichkeiten, die JUnit-Plattform auf Ihre Erweiterung aufmerksam zu machen (ein Vorgang, der als "Registrierung" bezeichnet wird). In diesem Artikel konzentrieren wir uns auf die declarative -Registrierung (d. H. Registrierung über Quellcode).
2. ParameterResolver
Das Injizieren von Parametern in Ihre Testmethoden kann mithilfe der JUnit 4-API durchgeführt werden, war jedoch recht begrenzt. Mit JUnit 5 kann die Jupiter-API durch die Implementierung von ParameterResolver erweitert werden, um Objekte beliebigen Typs für Ihre Testmethoden bereitzustellen. Werfen wir einen Blick.
2.1. FooParameterResolver
public class FooParameterResolver implements ParameterResolver {
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
return parameterContext.getParameter().getType() == Foo.class;
}
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
return new Foo();
}
}
Zuerst müssen wir ParameterResolver – implementieren, das zwei Methoden hat:
-
supportsParameter () - gibt "true" zurück, wenn der Typ des Parameters lautet
unterstützt (Foo in diesem Beispiel) und ** resolveParamater () - liefert ein Objekt des richtigen Typs (ein neues Objekt)
Foo-Instanz in diesem Beispiel), die dann in Ihre Testmethode eingefügt wird
2.2. FooTest
@ExtendWith(FooParameterResolver.class)
public class FooTest {
@Test
public void testIt(Foo fooInstance) {
//TEST CODE GOES HERE
}
}
Um die Erweiterung zu verwenden, müssen Sie sie dann über die Annotation @ ExtendWith (Zeile 1) deklarieren, d.
Wenn die JUnit-Plattform den Komponententest ausführt, erhält sie eine Foo -Instanz von FooParameterResolver und übergibt sie an die testIt () -Methode (Zeile 4).
Die Erweiterung hat einen Einflussbereich, der die Erweiterung aktiviert, je nachdem, wo ** sie deklariert ist
Die Erweiterung kann entweder aktiv sein bei:
-
Methodenebene, auf der es nur für diese Methode aktiv ist, oder
-
Klassenstufe, wo sie für die gesamte Testklasse aktiv ist, oder
@ Nested test class wie wir bald sehen werden
Hinweis: Sie sollten a ParameterResolver __at nicht für beide Bereiche für denselben Parametertyp deklarieren, da sich die JUnit-Plattform über diese Mehrdeutigkeit beschwert.
In diesem Artikel erfahren Sie, wie Sie zwei Erweiterungen schreiben und verwenden, um Person -Objekte einzufügen: Eine, die "gute" Daten (genannt ValidPersonParameterResolver ) und eine, die "schlechte" Daten ( InvalidPersonParameterResolver ) einfügt. Wir verwenden diese Daten, um eine Klasse namens PersonValidator zu testen, die den Status eines Person -Objekts überprüft.
3. Schreiben Sie die Erweiterungen
Nachdem wir nun verstanden haben, was eine ParameterResolver -Erweiterung ist, können wir jetzt schreiben:
-
eine, die gültige Person -Objekte zur Verfügung stellt
( ValidPersonParameterResolver ) und eine, die ungültige ** Person -Objekte enthält
( InvalidPersonParameterResolver )
3.1. ValidPersonParameterResolver
public class ValidPersonParameterResolver implements ParameterResolver {
public static Person[]VALID__PERSONS = {
new Person().setId(1L).setLastName("Adams").setFirstName("Jill"),
new Person().setId(2L).setLastName("Baker").setFirstName("James"),
new Person().setId(3L).setLastName("Carter").setFirstName("Samanta"),
new Person().setId(4L).setLastName("Daniels").setFirstName("Joseph"),
new Person().setId(5L).setLastName("English").setFirstName("Jane"),
new Person().setId(6L).setLastName("Fontana").setFirstName("Enrique"),
};
Beachten Sie das VALID PERSONS -Array von Person -Objekten. Dies ist das Repository gültiger Person -Objekte, aus denen eines ausgewählt wird, wenn die Methode resolveParameter () __ von der JUnit-Plattform aufgerufen wird.
Wenn Sie die gültigen Person-Objekte hier haben, werden zwei Dinge erreicht:
-
Trennung der Bedenken zwischen dem Gerätetest und den Daten, die das Laufwerk steuert
es . Wiederverwenden Sie, falls andere Komponententests gültige Person -Objekte zum Fahren erfordern
Sie
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
boolean ret = false;
if (parameterContext.getParameter().getType() == Person.class) {
ret = true;
}
return ret;
}
Wenn der Parametertyp Person ist, teilt die Erweiterung der JUnit-Plattform mit, dass sie diesen Parametertyp unterstützt. Andernfalls gibt sie den Wert false zurück.
Warum sollte das wichtig sein? Während die Beispiele in diesem Artikel einfach sind, können Unit-Test-Klassen in einer realen Anwendung sehr umfangreich und komplex sein, wobei viele Testmethoden unterschiedliche Parametertypen verwenden. Die JUnit-Plattform muss bei der Auflösung von Parametern innerhalb des aktuellen Einflussbereichs alle registrierten __ParameterResolver __s prüfen.
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
Object ret = null;
if (parameterContext.getParameter().getType() == Person.class) {
ret = VALID__PERSONS[new Random().nextInt(VALID__PERSONS.length)];
}
return ret;
}
Ein zufälliges Person -Objekt wird vom VALID PERSONS__-Array zurückgegeben.
Beachten Sie, wie resolveParameter () nur von der JUnit-Plattform aufgerufen wird, wenn supportsParameter () true zurückgibt.
3.2. InvalidPersonParameterResolver
public class InvalidPersonParameterResolver implements ParameterResolver {
public static Person[]INVALID__PERSONS = {
new Person().setId(1L).setLastName("Ad__ams").setFirstName("Jill,"),
new Person().setId(2L).setLastName(",Baker").setFirstName(""),
new Person().setId(3L).setLastName(null).setFirstName(null),
new Person().setId(4L).setLastName("Daniel&").setFirstName("{Joseph}"),
new Person().setId(5L).setLastName("").setFirstName("English, Jane"),
new Person()/** .setId(6L).setLastName("Fontana").setFirstName("Enrique")** /,
};
Beachten Sie das INVALID PERSONS -Array von Person -Objekten. Genau wie bei ValidPersonParameterResolver enthält diese Klasse einen Speicher für "schlechte" (d. H. Ungültige) Daten zur Verwendung durch Komponententests, um beispielsweise sicherzustellen, dass PersonValidator.ValidationExceptions__ bei ungültigen Daten ordnungsgemäß ausgelöst wird:
@Override
public Object resolveParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
Object ret = null;
if (parameterContext.getParameter().getType() == Person.class) {
ret = INVALID__PERSONS[new Random().nextInt(INVALID__PERSONS.length)];
}
return ret;
}
@Override
public boolean supportsParameter(ParameterContext parameterContext,
ExtensionContext extensionContext) throws ParameterResolutionException {
boolean ret = false;
if (parameterContext.getParameter().getType() == Person.class) {
ret = true;
}
return ret;
}
Der Rest dieser Klasse verhält sich natürlich genauso wie sein "guter" Gegenüber.
4. Erweiterungen deklarieren und verwenden
Jetzt, da wir zwei _ParameterResolver s haben, ist es an der Zeit, sie zu verwenden. Erstellen Sie eine JUnit-Testklasse für PersonValidator mit dem Namen PersonValidatorTest_ .
Wir verwenden mehrere Funktionen, die nur in JUnit Jupiter verfügbar sind:
-
@ DisplayName - dies ist der Name, der in den Testberichten angezeigt wird
viel lesbarer ** @ Nested - erstellt eine verschachtelte Testklasse mit einem eigenen Test
Lebenszyklus, getrennt von seiner übergeordneten Klasse ** @ RepeatedTest - der Test wird so oft wiederholt, wie angegeben
durch das Wertattribut (10 in jedem Beispiel)
Durch die Verwendung von @ Nested -Klassen können wir sowohl gültige als auch ungültige Daten in derselben Testklasse testen, wobei sie gleichzeitig vollständig voneinander entfernt sind:
@DisplayName("Testing PersonValidator")
public class PersonValidatorTest {
@Nested
@DisplayName("When using Valid data")
@ExtendWith(ValidPersonParameterResolver.class)
public class ValidData {
@RepeatedTest(value = 10)
@DisplayName("All first names are valid")
public void validateFirstName(Person person) {
try {
assertTrue(PersonValidator.validateFirstName(person));
} catch (PersonValidator.ValidationException e) {
fail("Exception not expected: " + e.getLocalizedMessage());
}
}
}
@Nested
@DisplayName("When using Invalid data")
@ExtendWith(InvalidPersonParameterResolver.class)
public class InvalidData {
@RepeatedTest(value = 10)
@DisplayName("All first names are invalid")
public void validateFirstName(Person person) {
assertThrows(
PersonValidator.ValidationException.class,
() -> PersonValidator.validateFirstName(person));
}
}
}
Beachten Sie, wie wir die Erweiterungen ValidPersonParameterResolver und InvalidPersonParameterResolver innerhalb derselben Haupttestklasse verwenden können, indem wir sie nur auf der Klassenebene @ Nested deklarieren. Versuchen Sie das mit JUnit 4! (Spoiler-Alarm: Sie können es nicht tun!)
5. Schlussfolgerung
In diesem Artikel haben wir untersucht, wie zwei ParameterResolver -Erweiterungen geschrieben werden, um gültige und ungültige Objekte bereitzustellen. Dann haben wir uns angesehen, wie man diese beiden ParameterResolver -Implementierungen in einem Unit-Test verwendet.
Der Code ist wie immer verfügbar: https://github.com/eugenp/tutorials/tree/master/testing-modules/junit-5 Über Github].
Wenn Sie mehr über das Erweiterungsmodell von JUnit Jupiter erfahren möchten, besuchen Sie das JUnit 5 Benutzerhandbuch oder https://www . ibm.com/developerworks/library/j-introducing-junit5-part1-jupiter-api/index.html Abschnitt 2 meines Tutorials zu developerWorks].