Bien, sé que probablemente quieras una propiedad fácil que puedas especificar en tu @BeforeClass o algo así, pero es posible que debamos esperar a que se implemente. Al menos tampoco pude encontrarlo.
Lo siguiente es feo como el infierno, pero creo que hace el trabajo, al menos en pequeña escala, se deja ver cómo se comporta en escenarios más complejos. Quizás con más tiempo, esto se puede mejorar en algo mejor.
Bien, entonces creé una clase de prueba similar a la tuya:
public class RetryTest extends TestConfig {
public class RetryTest extends TestConfig {
Assertion assertion = new Assertion();
@Test( enabled = true,
groups = { "retryTest" },
retryAnalyzer = TestRetry.class,
ignoreMissingDependencies = false)
public void testStep_1() {
}
@Test( enabled = true,
groups = { "retryTest" },
retryAnalyzer = TestRetry.class,
dependsOnMethods = "testStep_1",
ignoreMissingDependencies = false)
public void testStep_2() {
if (fail) assertion.fail("This will fail the first time and not the second.");
}
@Test( enabled = true,
groups = { "retryTest" },
retryAnalyzer = TestRetry.class,
dependsOnMethods = "testStep_2",
ignoreMissingDependencies = false)
public void testStep_3() {
}
@Test( enabled = true)
public void testStep_4() {
assertion.fail("This should leave a failure in the end.");
}
}
Tengo el Listener
en la súper clase solo en el caso de que me gustaría extender esto a otras clases, pero también puede configurar el oyente en su clase de prueba.
@Listeners(TestListener.class)
public class TestConfig {
protected static boolean retrySuccessful = false;
protected static boolean fail = true;
}
Tres de los 4 métodos anteriores tienen a RetryAnalyzer
. Lo dejé testStep_4
sin él para asegurarme de que lo que estoy haciendo a continuación no interfiera con el resto de la ejecución. Dicho RetryAnalyzer
no volverá a intentarlo (tenga en cuenta que el método regresa false
), pero hará lo siguiente:
public class TestRetry implements IRetryAnalyzer {
public static TestNG retryTestNG = null;
@Override
public boolean retry(ITestResult result) {
Class[] classes = {CreateBookingTest.class};
TestNG retryTestNG = new TestNG();
retryTestNG.setDefaultTestName("RETRY TEST");
retryTestNG.setTestClasses(classes);
retryTestNG.setGroups("retryTest");
retryTestNG.addListener(new RetryAnnotationTransformer());
retryTestNG.addListener(new TestListenerRetry());
retryTestNG.run();
return false;
}
}
Esto creará una ejecución dentro de su ejecución. No interferirá con el informe, y tan pronto como termine, continuará con su ejecución principal. Pero "reintentará" los métodos dentro de ese grupo.
Sí, lo sé, lo sé. Esto significa que ejecutará su conjunto de pruebas para siempre en un bucle eterno. Por eso el RetryAnnotationTransformer
. En él, eliminaremos el RetryAnalyzer de la segunda ejecución de esas pruebas:
public class RetryAnnotationTransformer extends TestConfig implements IAnnotationTransformer {
@SuppressWarnings("rawtypes")
@Override
public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
fail = false; // This is just for debugging. Will make testStep_2 pass in the second run.
annotation.setRetryAnalyzer(null);
}
}
Ahora tenemos el último de nuestros problemas. Nuestro conjunto de pruebas original no sabe nada sobre esa ejecución de "reintento" allí. Aquí es donde se pone realmente feo. Necesitamos decirle a nuestro reportero lo que acaba de suceder. Y esta es la parte que te animo a mejorar. Me falta el tiempo para hacer algo mejor, pero si puedo, lo editaré en algún momento.
Primero, necesitamos saber si la ejecución de retryTestNG fue exitosa. Probablemente hay un millón de formas de hacerlo mejor, pero por ahora esto funciona. Configuré un oyente solo para volver a intentar la ejecución. Puede verlo TestRetry
arriba y consta de lo siguiente:
public class TestListenerRetry extends TestConfig implements ITestListener {
(...)
@Override
public void onFinish(ITestContext context) {
if (context.getFailedTests().size()==0 && context.getSkippedTests().size()==0) {
successful = true;
}
}
}
Ahora el oyente de la suite principal, el que viste arriba en la superclase TestConfig
verá si sucedió la ejecución y si salió bien y actualizará el informe:
public class TestListener extends TestConfig implements ITestListener , ISuiteListener {
(...)
@Override
public void onFinish(ISuite suite) {
if (TestRetry.retryTestNG != null) {
for (ITestNGMethod iTestNGMethod : suite.getMethodsByGroups().get("retryTest")) {
Collection<ISuiteResult> iSuiteResultList = suite.getResults().values();
for (ISuiteResult iSuiteResult : iSuiteResultList) {
ITestContext iTestContext = iSuiteResult.getTestContext();
List<ITestResult> unsuccessfulMethods = new ArrayList<ITestResult>();
for (ITestResult iTestResult : iTestContext.getFailedTests().getAllResults()) {
if (iTestResult.getMethod().equals(iTestNGMethod)) {
iTestContext.getFailedTests().removeResult(iTestResult);
unsuccessfulMethods.add(iTestResult);
}
}
for (ITestResult iTestResult : iTestContext.getSkippedTests().getAllResults()) {
if (iTestResult.getMethod().equals(iTestNGMethod)) {
iTestContext.getSkippedTests().removeResult(iTestResult);
unsuccessfulMethods.add(iTestResult);
}
}
for (ITestResult iTestResult : unsuccessfulMethods) {
iTestResult.setStatus(1);
iTestContext.getPassedTests().addResult(iTestResult, iTestResult.getMethod());
}
}
}
}
}
}
El informe debe mostrar ahora 3 pruebas aprobadas (como fueron reintentadas) y una que falló porque no era parte de las otras 3 pruebas:
Sé que no es lo que estás buscando, pero te ayudo a que te sirva hasta que agreguen la funcionalidad a TestNG.