Reejecutar pruebas fallidas en TestNG

En este post, aprenderemos a reproducir casos de prueba fallidos utilizando TestNG. Exploraremos dos enfoques para conseguirlo, a saber: utilizando el archivo testng-failed.xml e implementando el IRetryAnalyzer de testNG.
Índice
Reinicia las pruebas fallidas utilizando el archivo testng-failed.xml
¿Cuándo utilizarlo?
A veces, como probadores de automatización de pruebas, sólo se nos pide que ejecutemos pruebas fallidas informadas por la herramienta de automatización de pruebas después de corregir algunos errores. Ejecutar sólo las pruebas fallidas verificará rápidamente las correcciones de errores.
¿Cómo conseguirlo?
Ejecutar sólo las pruebas fallidas es relativamente fácil, ya que TestNG proporciona un soporte natural para ello. Siempre que se ejecute un conjunto de pruebas utilizando el archivo testng.xml, se creará el archivo testng-failed.xml en la carpeta test-output después de que se ejecute la prueba. Después, podemos ejecutar este archivo igual que el archivo testng.xml. Como este archivo sólo almacena las pruebas fallidas, al ejecutarlo sólo se ejecutarán las pruebas fallidas.
Reintento automático de pruebas fallidas con IRetryAnalyzer
¿Cuándo utilizarlo?
A veces, en el informe de ejecución de la prueba aparecen fallos que no están causados por problemas en la aplicación. La causa de estos problemas puede estar relacionada con la configuración del entorno de prueba o con algún problema ocasional del servidor. Para asegurarnos de que los fallos notificados en el informe de prueba son reales y no sólo casos aislados, podemos volver a ejecutar los casos de prueba fallidos para eliminar los resultados de prueba falsos negativos en los informes de prueba.
¿Cómo conseguirlo?
Para reintentar automáticamente los casos de prueba fallidos durante la ejecución real de la prueba, necesitamos implementar la interfaz IRetryAnalyzer proporcionada por TestNG. La interfaz IRetryAnalyzer proporciona métodos para controlar la repetición de las pruebas. Aquí anularemos el método retry() de IRetryAnalyzer para asegurarnos de que la prueba se ejecuta en caso de fallo con el límite de reintentos especificado. Gracias a los comentarios del fragmento, es comprensible.
Fragmento
package com.artoftesting.test;
import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
public class RetryAnalyzer implements IRetryAnalyzer {
//Counter to keep track of retry attempts
int retryAttemptsCounter = 0;
//The max limit to retry running of failed test cases
//Set the value to the number of times we want to retry
int maxRetryLimit = 1;
//Method to attempt retries for failure tests
public boolean retry(ITestResult result) {
if (!result.isSuccess()) {
if(retryAttemptsCounter < maxRetryLimit){
retryAttemptsCounter++;
return true;
}
}
return false;
}
}
En el ejemplo, crearemos un método de prueba ficticio y lo haremos fallar intencionadamente mediante el método assert.fail(). Aquí establecemos el atributo retryAnalyzer de la anotación @Test utilizando la clase RetryAnalyzer.class que creamos anteriormente.
@Test(retryAnalyzer = RetryAnalyzer.class)
public void intentionallyFailingTest(){
System.out.println("Executing Test");
Assert.fail("Failing Test");
}
Resultado de la prueba
===============================================
Conjunto de pruebas
Total de pruebas realizadas: 2, Fallos: 1, Omisiones: 1
===============================================
Aquí podemos observar que el número de ejecuciones del método es de 2 con un fallo y un salto. La primera vez que falle el método de prueba, se marcará como omitido y, a continuación, la prueba volverá a ejecutarse gracias a la lógica proporcionada en RetryAnalyzer.
Ahora sólo hay un problema, necesitamos establecer el atributo retryAnalyzer en cada anotación @Test. Para solucionarlo, podemos implementar la interfaz IAnnotationTransformer. Esta interfaz permite cambiar la anotación testNG sobre la marcha. Así que creamos una clase que implemente la interfaz IAnnotationTransformer y hacemos que establezca RetryAnalyzer para las anotaciones @Test.
package com.ittester.test;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import org.testng.IAnnotationTransformer;
import org.testng.IRetryAnalyzer;
import org.testng.annotations.ITestAnnotation;
public class FailureRetryListener implements IAnnotationTransformer {
//Overriding the transform method to set the RetryAnalyzer
public void transform(ITestAnnotation testAnnotation, Class testClass,
Constructor testConstructor, Method testMethod) {
IRetryAnalyzer retry = testAnnotation.getRetryAnalyzer();
if (retry == null)
testAnnotation.setRetryAnalyzer(RetryAnalyzer.class);
}
}
Después de crear el listener, podemos especificarlo en el archivo testNG.xml de la siguiente manera-
<listeners>
<listener class-name="com.ittester.test.FailureRetryListener"/>
</listeners>
Ahora no tenemos que establecer retryAnalyzer en cada anotación @Test. Si tenemos un oyente especificado en el archivo testng.xml, funcionará para todas las pruebas.
PD: Si quieres añadir la funcionalidad de reintento sólo a un conjunto limitado de métodos de prueba, entonces basta con establecer la anotación @Test con el atributo retryAnalyzer utilizando RetryAnalyzer.class, no es necesario implementar IAnnotationTransformer y añadir un oyente en el archivo testng.xml.