Как повторно запустить неудачные тесты JUnit сразу?
есть ли способ иметь правило JUnit или что-то подобное, что дает каждому неудачному тесту второй шанс, просто пытаясь запустить его еще раз.
фон: у меня есть большой набор тестов Selenium2-WebDriver, написанных с помощью JUnit. Из-за очень агрессивного времени (только короткие периоды ожидания после щелчков) некоторые тесты (1 из 100, и всегда другой) могут потерпеть неудачу, потому что сервер иногда отвечает немного медленнее. Но я не могу сделать период ожидания так долго, что это, безусловно, достаточно долго, потому что тогда тесты не пройду.) -- Поэтому я думаю, что это приемлемо для этого случая использования, что тест зеленый, даже если он нуждается во второй попытке.
конечно, было бы лучше иметь 2 из 3 большинства (повторите неудачный тест 3 раза и примите их как правильные, если два из тестов правильны), но это было бы будущим улучшением.
4 ответов:
вы можете сделать это с помощью TestRule. Это даст вам необходимую гибкость. А TestRule позволяет вставить логику вокруг теста, так вам будет выполнять цикл повторить:
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"); } }сердце a
TestRuleэтоbase.evaluate(), который вызывает ваш метод тестирования. Поэтому вокруг этого вызова вы ставите цикл повтора. Если в вашем методе тестирования возникает исключение (ошибка утверждения на самом деле являетсяAssertionError), то тест не удался, и вы будете повторять попытку.там еще одна вещь, которая может оказаться полезной. Вы можете только применить эту логику повтора к набору тестов, и в этом случае вы можете добавить в класс повтора выше теста для определенной аннотации метода.
Descriptionсодержит список аннотаций для метода. Для получения дополнительной информации об этом см. Мой ответ на как запустить некоторый код перед каждым методом JUnit @Test индивидуально, без использования @RunWith или AOP?.С помощью настраиваемого TestRunner
Это предложение CKuck, вы можете определить свой собственный Бегун. Вам нужно расширить BlockJUnit4ClassRunner и переопределить runChild(). Для получения дополнительной информации см. Мой ответ на Как определить правило метода JUnit в комплекте?. В этом ответе подробно описывается, как определить, как запускать код для каждого метода в наборе, для которого вы должны определить свой собственный Бегун.
как для меня писать пользовательский бегун более гибкое решение. Решение, которое опубликовано выше (с примером кода) имеет два недостатка:
- он не будет повторять тест, если он не работает на этапе @BeforeClass;
- это вычислительные тесты работают немного по-другому (когда у вас есть 3 попытки, вы получите тестовые прогоны: 4, успех 1, что может привести к путанице);
вот почему я предпочитаю больше подход с написанием пользовательского бегуна. И код изготовленного на заказ бегуна смог будьте следующие:
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); } }
теперь есть лучший вариант. Если вы используете Плагины maven, такие как: surfire или failsefe, есть возможность добавить параметр
rerunFailingTestsCountSurFire Api. Этот материал был реализован в следующем билете:С Jira Билет. В этом случае вам не нужно писать свой собственный код и плагин автоматически изменять отчет о результатах тестирования.
Я вижу только один недостаток этого подхода: если какой-то тест не прошел до/после этапа класса, тест не будет повторно запущен.
вы должны написать свой собственный
org.junit.runner.Runnerи аннотировать свои тесты с@RunWith(YourRunner.class).
Comments