Tests in JUnit 4 bedingt ignorieren

365

OK, die @IgnoreAnmerkung ist also gut, um zu markieren, dass ein Testfall nicht ausgeführt werden sollte.

Manchmal möchte ich jedoch einen Test ignorieren, der auf Laufzeitinformationen basiert. Ein Beispiel könnte sein, wenn ich einen Parallelitätstest habe, der auf einem Computer mit einer bestimmten Anzahl von Kernen ausgeführt werden muss. Wenn dieser Test auf einem Einprozessor-Computer ausgeführt würde, wäre es meiner Meinung nach nicht richtig, den Test nur zu bestehen (da er nicht ausgeführt wurde), und es wäre sicherlich nicht richtig, den Test nicht zu bestehen und den Build zu brechen .

Daher möchte ich in der Lage sein, Tests zur Laufzeit zu ignorieren, da dies das richtige Ergebnis zu sein scheint (da das Testframework den Build bestehen lässt, aber aufzeichnet, dass die Tests nicht ausgeführt wurden). Ich bin mir ziemlich sicher, dass die Anmerkung mir diese Flexibilität nicht bietet, und vermute, dass ich die Testsuite für die betreffende Klasse manuell erstellen muss. In der Dokumentation wird jedoch nichts darüber erwähnt, und beim Durchsuchen der API ist auch nicht klar, wie dies programmgesteuert erfolgen würde (dh wie erstelle ich programmgesteuert eine Instanz Testoder ähnliches, die der durch die @IgnoreAnnotation erstellten entspricht?).

Wenn jemand in der Vergangenheit etwas Ähnliches getan hat oder eine gute Vorstellung davon hat, wie ich sonst vorgehen könnte, würde ich mich freuen, davon zu hören.

Andrzej Doyle
quelle

Antworten:

476

Die JUnit-Methode besteht darin, dies zur Laufzeit zu tun org.junit.Assume.

 @Before
 public void beforeMethod() {
     org.junit.Assume.assumeTrue(someCondition());
     // rest of setup.
 }

Sie können dies in einer @BeforeMethode oder im Test selbst tun , jedoch nicht in einer @AfterMethode. Wenn Sie dies im Test selbst tun, wird Ihre @BeforeMethode ausgeführt. Sie können dies auch innerhalb tun, @BeforeClassum eine Klasseninitialisierung zu verhindern.

Ein Annahmefehler führt dazu, dass der Test ignoriert wird.

Bearbeiten: Zum Vergleich mit der @RunIfAnmerkung von junit-ext würde der Beispielcode folgendermaßen aussehen:

@Test
public void calculateTotalSalary() {
    assumeThat(Database.connect(), is(notNull()));
    //test code below.
}

Ganz zu schweigen davon, dass es viel einfacher ist, die Verbindung von der Database.connect()Methode auf diese Weise zu erfassen und zu verwenden .

Yishai
quelle
1
@notnoop, das ist überhaupt nicht meine Beobachtung. Sie werden ignoriert. Der IDEA-Testläufer meldet sie auf diese Weise, und ein Blick auf den JUnit-Quellcode zeigt, dass der Test als ignoriert gemeldet wird.
Yishai
1
Zitat: "In Zukunft kann sich dies ändern, und eine fehlgeschlagene Annahme kann dazu führen, dass der Test ignoriert wird." Es hat sich tatsächlich geändert, ab 4.5 glaube ich. Der aktuelle Javadoc sagt: "Der Standard-JUnit-Läufer behandelt Tests mit fehlgeschlagenen Annahmen als ignoriert. Benutzerdefinierte Läufer verhalten sich möglicherweise anders." github.com/KentBeck/junit/blob/…
Yishai
4
Eclipse 3.6 mit Junit 4.8.1 meldet falsche Annahmen als bestandenen Test. Gleiches gilt für Ameise 1.8.1.
Fijiaaron
8
Dass Eclipse fehlgeschlagene Annahmen als bestanden meldet, ist ein Fehler: bugs.eclipse.org/bugs/show_bug.cgi?id=359944
Martin
1
@ JeffStorey, dann suchst du ein paar Dinge. Eine ist die @BeforeClassAnnotation, bei der Ihre Annahme dort fehlschlagen kann, wodurch die gesamte Klasse übersprungen wird. Eine andere ist @ClassRule(für die feinkörnige Kontrolle, aber einmal über die gesamte Klasse).
Yishai
51

Sie sollten das Junit-extProjekt auschecken . Sie haben RunIfAnmerkungen, die bedingte Tests durchführen, wie:

@Test
@RunIf(DatabaseIsConnected.class)
public void calculateTotalSalary() {
    //your code there
}

class DatabaseIsConnected implements Checker {
   public boolean satisify() {
        return Database.connect() != null;
   }
}

[Codebeispiel aus ihrem Tutorial]

notnoop
quelle
3
Vielen Dank für diese Antwort - eine interessante alternative Syntax für die Funktionalität, obwohl ich Assumedirekt darauf eingehen werde, um keine weitere Abhängigkeit einzuführen.
Andrzej Doyle
3
Ich persönlich bevorzuge diese Lösung. Wenn Sie viele Tests haben, die unter denselben Bedingungen ausgeführt werden sollten, ist dies weitaus idealer, als Assume in jedem Test verwenden zu müssen. Wenn dies auf Klassenebene und nicht auf Methodenebene verwendet werden kann, ist es sogar noch idealer.
Richard
Ich würde es vorziehen, weil dies hilft, den Test zur Laufzeit bedingt auszuführen. Es ist geeignet, wo eine Reihe von Komponententests ausgeführt werden sollen, und die Anforderung besteht darin, die Komponententests auf einem bestimmten Prüfer durchzuführen. Ich war wirklich erstaunt zu sehen, dass junit-ext nicht im Maven-Repository verfügbar ist. Wie würden wir dies im Maven-Projekt nutzen können?
Shambhu
4
Eine Annotation wie @RunIftrennt die Bedingung, wann ein Test ausgeführt werden soll, vom eigentlichen Testcode, was ich für gut halte. Was ich nicht mag ist, dass es einen bestimmten Testläufer erfordert. Deshalb habe ich eine JUnit-Regel geschrieben, um Tests unbedingt zu ignorieren.
Rüdiger Herrmann
2
Nach der Installation des junit-ext- JARs (hier Code.google.com/p/junit-ext/downloads/… ) in unserem lokalen Repository und der Implementierung dieser @ RunIf-Annotation ... nichts! Es wird völlig ignoriert, und ich denke, der Grund könnte sein, dass junit-ext von junit 4.5 abzuhängen scheint. Wir brauchen 4,9+ wegen des Frühlingstests. Also ... egal.
Marc
7

In JUnit 4 können Sie auch eine Anmerkung erstellen, um anzugeben, dass der Test Ihren benutzerdefinierten Kriterien entsprechen muss. Erweitern Sie dann den Standardläufer um Ihren eigenen und stützen Sie Ihre Entscheidung mithilfe von Reflection auf die benutzerdefinierten Kriterien. Es könnte ungefähr so ​​aussehen:

public class CustomRunner extends BlockJUnit4ClassRunner {
    public CTRunner(Class<?> klass) throws initializationError {
        super(klass);
    }

    @Override
    protected boolean isIgnored(FrameworkMethod child) {
        if(shouldIgnore()) {
            return true;
        }
        return super.isIgnored(child);
    }

    private boolean shouldIgnore(class) {
        /* some custom criteria */
    }
}
Kyle Shrader
quelle
Dies sieht zwar gut und sauber aus, funktioniert aber mit aktuellen Versionen von JUnit4 nicht, da BlockJUnit4ClassRunnerdie isIgnoredMethode nicht mehr angeboten wird.
Dave
-2

Ein kurzer Hinweis: Assume.assumeTrue(condition)Ignoriert die restlichen Schritte, besteht aber den Test. Verwenden Sie org.junit.Assert.fail()die bedingte Anweisung , um den Test nicht zu bestehen . Funktioniert genauso wie Assume.assumeTrue(), besteht den Test jedoch nicht.

TIn TIn
quelle
5
Wie in den obigen Antworten erwähnt, führt eine fehlgeschlagene Annahme nicht dazu, dass der Test bestanden wird, sondern gibt einen separaten Status zurück. Einige Läufer melden dies möglicherweise fälschlicherweise als bestanden, aber dies ist eine Schwäche / ein Fehler im Testläufer (und der Standard-JUnit-Läufer zeigt den Test als ignoriert an). Und was Ihren letzten Satz betrifft, ist das Nichtbestehen des Tests nicht das, was ich (ed) tun möchte.
Andrzej Doyle
Oh ok. Die Tests bestanden in meinem Fall unter fehlgeschlagener Annahme, aber ich wollte, dass sie als fehlgeschlagen gemeldet werden (ich habe nach einer Ausnahme von Test Watcher gesucht). Einen Fehler zu erzwingen hat mir geholfen.
TIn TIn