unitarias unitaria tutorial test software pruebas prueba para español ejercicios configuracion con java testing junit

java - unitaria - test junit netbeans



¿Cómo volver a ejecutar pruebas JUnit fallidas inmediatamente? (4)

Ahora hay una mejor opción. Si está utilizando complementos maven como: surfire o failsefe, existe la opción de agregar el parámetro rerunFailingTestsCount SurFire Api . Esto se implementó en el siguiente ticket: Jira Ticket . En este caso, no necesita escribir su código personalizado y el complemento modifica automáticamente el informe de resultados de la prueba.
Solo veo una desventaja de este enfoque: si se falla alguna prueba en la prueba de la etapa Antes / Después de la clase no se volverá a ejecutar.

¿Hay alguna manera de tener una Regla JUnit o algo similar que dé a cada prueba fallida una segunda oportunidad, simplemente tratando de ejecutarla una vez más?

Antecedentes: tengo un gran conjunto de pruebas Selenium2-WebDriver escritas con JUnit. Debido a una sincronización muy agresiva (solo períodos cortos de espera después de los clics) algunas pruebas (1 de cada 100, y siempre una diferente) pueden fallar porque el servidor a veces responde un poco más lento. Pero no puedo hacer que el período de espera sea tan largo que definitivamente es suficiente, porque entonces las pruebas durarán para siempre.) - Entonces creo que es aceptable para este caso de uso que una prueba sea verde incluso si necesita un segundo tratar.

Por supuesto, sería mejor tener una mayoría de 2 de 3 (repetir una prueba de falla 3 veces, y tomarlas como correctas, si dos de las pruebas son correctas), pero esto sería una mejora futura.


En cuanto a mí, escribir corredor personalizado solución más flexible. La solución que se publicó arriba (con el ejemplo de código) tiene dos desventajas:

  1. No volverá a probar si falla en la etapa @BeforeClass;
  2. Las pruebas de cálculo se ejecutan de forma un poco diferente (cuando tiene 3 intentos, recibirá ejecuciones de prueba: 4, éxito 1 que podría ser confuso);

Es por eso que prefiero más enfoque con la escritura de corredor personalizado. Y el código del corredor personalizado podría estar siguiendo:

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); } }


Puedes hacer esto con una TestRule . Esto le dará la flexibilidad que necesita. Una TestRule le permite insertar lógica alrededor de la prueba, por lo que implementaría el ciclo de reintento:

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"); } }

El corazón de un TestRule es el base.evaluate() , que llama a su método de prueba. Entonces alrededor de esta llamada pones un ciclo de reintento. Si se lanza una excepción en su método de prueba (una falla de aserción es en realidad un AssertionError ), entonces la prueba ha fallado, y usted volverá a intentarlo.

Hay otra cosa que puede ser de utilidad. Puede que solo desee aplicar esta lógica de reintento a un conjunto de pruebas, en cuyo caso puede agregar a la clase Reintentar por encima de una prueba para una anotación particular del método. Description contiene una lista de anotaciones para el método. Para obtener más información al respecto, consulte mi respuesta a Cómo ejecutar un código antes de cada JUnit @Test método individualmente, sin usar @RunWith ni AOP? .

Usando un TestRunner personalizado

Esta es la sugerencia de CKuck, puedes definir tu propio Runner. Necesita extender BlockJUnit4ClassRunner y anular runChild (). Para obtener más información, consulte mi respuesta a ¿Cómo definir la regla de método JUnit en un conjunto? . Esta respuesta detalla cómo definir cómo ejecutar código para cada método en una Suite, para lo cual debe definir su propio Runner.


Tienes que escribir tu propio org.junit.runner.Runner y anotar tus pruebas con @RunWith(YourRunner.class) .