In diesem Beitrag hat mein Kollege Barend gezeigt, wie man bestimmte Tests in JUnit bedingungslos ignorieren kann. In diesem Beitrag sehen wir uns an, wie dies in TestNG, einem anderen beliebten Test-Framework, gelöst werden kann.
Im Gegensatz zu JUnit verfügt TestNG nicht über die Klasse Assume. Es stellt jedoch einen Ausnahmetyp namens SkipException zur Verfügung, der dem TestNG-Framework mitteilt, dass eine Testmethode als übersprungen betrachtet werden sollte.
Mit diesem Ausnahmetyp ist es relativ einfach, Ihre eigene Assume-Klasse zu schreiben:
[code lang="java"]
public class Assumes {
public static <T> void assumeThat(T actual, Matcher<? super T matcher) {assumeThat("", actual, matcher);}public static T void assumeThat(String reason, T actual, Matcher? super T > matcher) {
if (!matcher.matches(actual)) {
Beschreibung description = new StringDescription();
description.appendText(Grund)
.appendText("nErwartet: ")
.appendDescriptionOf(matcher)
.appendText("n aber: ");
matcher.describeMismatch(actual, description);
throw new SkipException(description.toString());
}
}
public static void assumeThat(String reason, boolean assertion) {
if (!assertion) {
throw new SkipException(Grund);
}
}
}
[/code]
Wenn der Hamcrest Matcher fehlschlägt, wird die SkipException ausgelöst und TestNG überspringt die kommentierte Testmethode.
Eine andere Möglichkeit, dies in TestNG zu erreichen, entspricht eher dem, was Barend mit dem TestRunner gemacht hat. In TestNG können Sie das Verhalten des Frameworks mit Hilfe von TestListeners einfach hinzufügen und/oder ändern. Um die Annahmen in einem Listener zu überprüfen, können wir die Testmethoden mit einer neuen Annotation versehen:
[code lang="java"]
@Retention(RUNTIME)
public @interface Annahme {
String[] methods();
}
[/code]
Die Annotation enthält einen Verweis auf Methoden, die einen booleschen Wert zurückgeben, der angibt, ob die Annahme zutrifft oder nicht. Zum Beispiel, wenn wir einen Test nur durchführen wollen, wenn die Außentemperatur hoch genug ist:
[code lang="java"]
@Test
[/code] @Assumption(methods = "isNiceOutside")
public void shouldNotBeRunTooCold() {
....
}
public boolean isNiceOutside() {
return sensor.temperature() > 25;
}
Damit diese Annotation von TestNG aufgegriffen wird, müssen wir die Klasse mit einem Listener annotieren. Einer der Listener-Typen, die TestNG zur Verfügung stellt, ist der InvokedMethodListener. Dieser Listener wird informiert, wenn TestNG im Begriff ist, eine Methode auszuführen, und wenn die Methode beendet ist. So können wir uns einklinken, prüfen, ob die Methode annotiert ist, und die Annahmen überprüfen, indem wir die Methode bei Bedarf überspringen. Lassen Sie uns implementieren:
[code lang="java"]
public class AssumptionListener implements IInvokedMethodListener {
@Override
public void beforeInvocation(IInvokedMethod invokedMethod, ITestResult result) {
ITestNGMethod testNgMethod = result.getMethod();
ConstructorOrMethod contructorOrMethod = testNgMethod.getConstructorOrMethod();
Methode method = contructorOrMethod.getMethod();
if (method == null || !method.isAnnotationPresent(Assumption.class)) {
zurück;
}
Liste<Zeichenfolge> failedAssumptions = checkAssumptions(Methode, Ergebnis);
if (!failedAssumptions.isEmpty()) {
throw new SkipException(format("Überspringen [%s], weil die %s-Annahme(n) nicht zutreffen.", contructorOrMethod.getName(), failedAssumptions));
}
}
@Override
public void afterInvocation(IInvokedMethod method, ITestResult testResult) {
}
private Liste<Zeichenfolge> checkAssumptions(Methode methode, ITestResult ergebnis) {
Annahme annotation = method.getAnnotation(Assumption.class);
String[] assumptionMethods = annotation.methods();
Liste<Zeichenfolge> failedAssumptions = new ArrayList<Zeichenfolge>();
Klasse clazz = result.getMethod().getTestClass().getRealClass();
for (String assumptionMethod : assumptionMethods) {
boolean assume = checkAssumption(result, clazz, assumptionMethod);
if (!assume) {
failedAssumptions.add(assumptionMethod);
}
}
return failedAssumptions;
}
private boolean checkAssumption(ITestResult result, Class clazz, String assumptionMethod) {
versuchen {
Methode assumption = clazz.getMethod(assumptionMethod);
if (assumption.getReturnType() != boolean.class) {
throw new RuntimeException(format("Annahmemethode [%s] sollte einen Boolean zurückgeben", assumptionMethod));
}
return (Boolean) assumption.invoke(result.getInstance());
} catch (Exception e) {
...
}
}
}
[/code]
Wenn wir nun zu unserem Beispiel zurückkehren, können wir es auf folgende Weise beenden:
[code lang="java"]
@Listeners(value = AssumptionListener.class)
public void WeatherTest {
@Test
@Assumption(methods = "isNiceOutside")
public void shouldNotBeRunTooCold() {
....
}
public boolean isNiceOutside() {
return sensor.temperature() > 25;
[/code] }
}
Beide Annahmen sind in meinem Lieblingsprojekt auf Github namens AssumeNG implementiert worden. Die jar-Datei finden Sie auch im zentralen Maven-Repository zur einfachen Einbindung in Ihr Projekt (nl.javadude.assumeng:assumeng:1.2.2).