Gibt es eine Möglichkeit, eine JUnit-Regel oder ähnliches zu haben, die jedem fehlgeschlagenen Test eine zweite Chance gibt, indem Sie einfach versuchen, ihn erneut auszuführen?
Hintergrund: Ich habe eine große Anzahl von Selenium2-WebDriver-Tests mit JUnit geschrieben. Aufgrund eines sehr aggressiven Timings (nur kurze Wartezeiten nach den Klicks) können einige Tests (1 von 100 und immer ein anderer) fehlschlagen, da der Server manchmal etwas langsamer reagiert. Aber ich kann die Wartezeit nicht so lang machen, dass sie definitiv lang genug ist, denn dann werden die Tests ewig dauern.) - Ich denke, es ist für diesen Anwendungsfall akzeptabel, dass ein Test grün ist, selbst wenn er eine Sekunde benötigt Versuchen.
Natürlich wäre es besser, eine 2 von 3 Mehrheit zu haben (wiederholen Sie einen fehlgeschlagenen Test dreimal und nehmen Sie sie als richtig, wenn zwei der Tests korrekt sind), aber dies wäre eine zukünftige Verbesserung.
Antworten:
Sie können dies mit einer TestRule tun . Dies gibt Ihnen die Flexibilität, die Sie benötigen. Mit einer TestRule können Sie Logik um den Test einfügen, sodass Sie die Wiederholungsschleife implementieren würden:
public class RetryTest { public class Retry implements TestRule { private int retryCount; public Retry(int retryCount) { this.retryCount = retryCount; } public Statement apply(Statement base, Description description) { return statement(base, description); } private Statement statement(final Statement base, final Description description) { return new Statement() { @Override public void evaluate() throws Throwable { Throwable caughtThrowable = null; // implement retry logic here for (int i = 0; i < retryCount; i++) { try { base.evaluate(); return; } catch (Throwable t) { caughtThrowable = t; System.err.println(description.getDisplayName() + ": run " + (i+1) + " failed"); } } System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures"); throw caughtThrowable; } }; } } @Rule public Retry retry = new Retry(3); @Test public void test1() { } @Test public void test2() { Object o = null; o.equals("foo"); } }
Das Herzstück von a
TestRule
ist dasbase.evaluate()
, das Ihre Testmethode aufruft. Um diesen Aufruf herum setzen Sie eine Wiederholungsschleife. Wenn in Ihrer Testmethode eine Ausnahme ausgelöst wird (ein Assertionsfehler ist tatsächlich ein FehlerAssertionError
), ist der Test fehlgeschlagen, und Sie werden es erneut versuchen.Es gibt noch eine andere Sache, die von Nutzen sein kann. Möglicherweise möchten Sie diese Wiederholungslogik nur auf eine Reihe von Tests anwenden. In diesem Fall können Sie der Wiederholungsklasse über einem Test eine bestimmte Anmerkung zur Methode hinzufügen.
Description
enthält eine Liste von Anmerkungen für die Methode. Weitere Informationen hierzu finden Sie in meiner Antwort auf So führen Sie Code vor jeder JUnit @ Test-Methode einzeln aus, ohne @RunWith oder AOP zu verwenden. .Verwenden eines benutzerdefinierten TestRunner
Dies ist der Vorschlag von CKuck, Sie können Ihren eigenen Runner definieren. Sie müssen BlockJUnit4ClassRunner erweitern und runChild () überschreiben. Weitere Informationen finden Sie in meiner Antwort auf Definieren der JUnit-Methodenregel in einer Suite. . Diese Antwort beschreibt, wie Sie definieren, wie Code für jede Methode in einer Suite ausgeführt wird, für die Sie Ihren eigenen Runner definieren müssen.
quelle
Jetzt gibt es eine bessere Option. Wenn Sie Maven-Plugins wie Surfire oder Failsefe verwenden, können Sie den Parameter
rerunFailingTestsCount
SurFire Api hinzufügen . Dieses Zeug wurde im folgenden Ticket implementiert: Jira Ticket . In diesem Fall müssen Sie Ihren benutzerdefinierten Code nicht schreiben und das Plugin ändert den Testergebnisbericht automatisch.Ich sehe nur einen Nachteil dieses Ansatzes: Wenn ein Test vor / nach dem Unterricht fehlschlägt, wird der Test nicht erneut ausgeführt.
quelle
Wie für mich schreiben benutzerdefinierte Läufer flexiblere Lösung. Die oben beschriebene Lösung (mit Codebeispiel) hat zwei Nachteile:
Deshalb bevorzuge ich mehr Ansatz beim Schreiben von benutzerdefinierten Läufern. Der Code des benutzerdefinierten Läufers könnte folgender sein:
import org.junit.Ignore; import org.junit.internal.AssumptionViolatedException; import org.junit.internal.runners.model.EachTestNotifier; import org.junit.runner.Description; import org.junit.runner.notification.RunNotifier; import org.junit.runner.notification.StoppedByUserException; import org.junit.runners.BlockJUnit4ClassRunner; import org.junit.runners.model.FrameworkMethod; import org.junit.runners.model.InitializationError; import org.junit.runners.model.Statement; public class RetryRunner extends BlockJUnit4ClassRunner { private final int retryCount = 100; private int failedAttempts = 0; public RetryRunner(Class<?> klass) throws InitializationError { super(klass); } @Override public void run(final RunNotifier notifier) { EachTestNotifier testNotifier = new EachTestNotifier(notifier, getDescription()); Statement statement = classBlock(notifier); try { statement.evaluate(); } catch (AssumptionViolatedException e) { testNotifier.fireTestIgnored(); } catch (StoppedByUserException e) { throw e; } catch (Throwable e) { retry(testNotifier, statement, e); } } @Override protected void runChild(final FrameworkMethod method, RunNotifier notifier) { Description description = describeChild(method); if (method.getAnnotation(Ignore.class) != null) { notifier.fireTestIgnored(description); } else { runTestUnit(methodBlock(method), description, notifier); } } /** * Runs a {@link Statement} that represents a leaf (aka atomic) test. */ protected final void runTestUnit(Statement statement, Description description, RunNotifier notifier) { EachTestNotifier eachNotifier = new EachTestNotifier(notifier, description); eachNotifier.fireTestStarted(); try { statement.evaluate(); } catch (AssumptionViolatedException e) { eachNotifier.addFailedAssumption(e); } catch (Throwable e) { retry(eachNotifier, statement, e); } finally { eachNotifier.fireTestFinished(); } } public void retry(EachTestNotifier notifier, Statement statement, Throwable currentThrowable) { Throwable caughtThrowable = currentThrowable; while (retryCount > failedAttempts) { try { statement.evaluate(); return; } catch (Throwable t) { failedAttempts++; caughtThrowable = t; } } notifier.addFailure(caughtThrowable); } }
quelle
Sie müssen Ihre eigenen schreiben
org.junit.runner.Runner
und Ihre Tests mit Anmerkungen versehen@RunWith(YourRunner.class)
.quelle
Der vorgeschlagene Kommentar wurde basierend auf diesem Artikel mit einigen Ergänzungen geschrieben.
Wenn ein Testfall aus Ihrem jUnit-Projekt das Ergebnis "Fehler" oder "Fehler" erhält, wird dieser Testfall noch einmal ausgeführt. Insgesamt haben wir hier 3 Chancen gesetzt, um ein Erfolgsergebnis zu erzielen.
Wir müssen also eine Regelklasse erstellen und Ihrer Testklasse "@Rule" -Nachrichten hinzufügen .
Wenn Sie nicht für jede Ihrer Testklassen dieselben "@ Rule" -Nachrichten schreiben möchten, können Sie diese zu Ihrer abstrakten SetProperty-Klasse hinzufügen (falls vorhanden) und von dieser erweitern.
Regelklasse:
import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; public class RetryRule implements TestRule { private int retryCount; public RetryRule (int retryCount) { this.retryCount = retryCount; } public Statement apply(Statement base, Description description) { return statement(base, description); } private Statement statement(final Statement base, final Description description) { return new Statement() { @Override public void evaluate() throws Throwable { Throwable caughtThrowable = null; // implement retry logic here for (int i = 0; i < retryCount; i++) { try { base.evaluate(); return; } catch (Throwable t) { caughtThrowable = t; // System.out.println(": run " + (i+1) + " failed"); System.err.println(description.getDisplayName() + ": run " + (i + 1) + " failed."); } } System.err.println(description.getDisplayName() + ": giving up after " + retryCount + " failures."); throw caughtThrowable; } }; } }
Testklasse:
import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; import org.openqa.selenium.WebDriver; import org.openqa.selenium.firefox.FirefoxDriver; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; /** * Created by ONUR BASKIRT on 27.03.2016. */ public class RetryRuleTest { static WebDriver driver; final private String URL = "http://www.swtestacademy.com"; @BeforeClass public static void setupTest(){ driver = new FirefoxDriver(); } //Add this notification to your Test Class @Rule public RetryRule retryRule = new RetryRule(3); @Test public void getURLExample() { //Go to www.swtestacademy.com driver.get(URL); //Check title is correct assertThat(driver.getTitle(), is("WRONG TITLE")); } }
quelle