2011-10-18 12 views
5

Para mi pasantía, tengo que usar TestNG y selenio para probar una aplicación web. Pero tengo un problema, a veces el selenio o el navegador no funciona por algún motivo aleatorio, por lo que una prueba de trabajo se marca como "fallida". Para evitar eso, puedo usar la anotación @Test(invocationCount = 4, successPercentage = 25), y si la prueba se realiza una vez, la prueba está marcada como "Salir bien", eso es bueno, pero el problema es que esta solución multiplica el tiempo de prueba por 4, esto no es muy eficiente .Cómo optimizar las pruebas de detección y selenio

Lo que puedo hacer para disminuir el tiempo de prueba, es escribir una regla "si la prueba falló, vuelva a ejecutar esta prueba (y solo si la prueba falló) y si funcionó la segunda, tercera o cuarta vez, marque esta prueba como "tener éxito" "Para poder evitar estos errores al azar. Pero no he encontrado cómo escribir esta regla, vi que podemos agregar un oyente, por lo que tenemos un método llamado "onTestFailure" para poder hacer algo cuando la prueba ha fallado pero no sé cómo volver a hacerlo. ejecuta la prueba.

También encontré testng-failed.xml donde se guardan todas las pruebas fallidas, por lo que podemos ejecutar este archivo xml para volver a ejecutar estas pruebas, pero esto borrará el informe de la primera ejecución anterior, pero quiero que el las pruebas fallidas se marcan como "exitosas" si la segunda ejecución es exitosa. (He integrado testNG/selenio a Jenkins, entonces tengo un gráfico con todas las pruebas, por lo que este método no es muy adecuado, pero este método no multiplica el tiempo de prueba por 4 y esto es lo que quiero)

Entonces, si tienes alguna pista de cómo hacerlo, sería muy agradable.

+0

He probado para ejecutar el testng-failed.xml 3 veces, y luego todas las pruebas están funcionando, y eso no lleva mucho tiempo. Pero con Jenkins, cuando el testng-failed.xml se ejecuta la última vez, esto editará el testng-result.xml, por lo que ahora el gráfico indica "1 ejecución de prueba, 1 tiene éxito", porque la última ejecución, testng solo se ha iniciado esta prueba que falló las 3 primeras veces. Este método generará un gráfico con toda la prueba fallida, pero no se indicarán todas las pruebas de funcionamiento (excepto las pruebas que se ejecutan por tercera vez), no es exactamente lo que quiero ... ¿Alguna pista? – user1000499

Respuesta

4

Puede utilizar una combinación del IRetryAnalyzer, un Listener y un reportero personalizado para hacer lo que está buscando.

IRetryAnalyzer:

public class RetryAnalyzer implements IRetryAnalyzer { 
private int count = 0; 
// this number is actually twice the number 
// of retry attempts will allow due to the retry 
// method being called twice for each retry 
private int maxCount = 6; 
protected Logger log; 
private static Logger testbaseLog; 

static { 
    PropertyConfigurator.configure("test-config/log4j.properties"); 
    testbaseLog = Logger.getLogger("testbase.testng"); 
} 

public RetryAnalyzer() 
{ 
    testbaseLog.trace(" ModeledRetryAnalyzer constructor " + this.getClass().getName()); 
    log = Logger.getLogger("transcript.test"); 
} 

@Override 
public boolean retry(ITestResult result) { 
    testbaseLog.trace("running retry logic for '" 
      + result.getName() 
      + "' on class " + this.getClass().getName()); 
     if(count < maxCount) {      
       count++;          
       return true; 
     } 
     return false; 
} 
} 

RetryListener:

public class RetryTestListener extends TestListenerAdapter { 
private int count = 0; 
private int maxCount = 3; 

@Override 
public void onTestFailure(ITestResult result) {  
    Logger log = Logger.getLogger("transcript.test"); 
    Reporter.setCurrentTestResult(result); 

    if(result.getMethod().getRetryAnalyzer().retry(result)) {  
     count++; 
     result.setStatus(ITestResult.SKIP); 
     log.warn("Error in " + result.getName() + " with status " 
       + result.getStatus()+ " Retrying " + count + " of 3 times"); 
     log.info("Setting test run attempt status to Skipped");     
    } 
    else 
    { 
     count = 0; 
     log.error("Retry limit exceeded for " + result.getName()); 
    }  

    Reporter.setCurrentTestResult(null); 
} 

@Override 
public void onTestSuccess(ITestResult result) 
{ 
    count = 0; 
} 

Sin embargo, parece que hay un error dentro de TestNG que realmente hace que algunos de los resultados de la prueba, debe notificarse como tanto saltado y han fracasado. Para evitar esto, recomiendo que puede modificar el que Reportero que desea utilizar e incluir un método como el que se incluye a continuación:

private IResultMap removeIncorrectlyFailedTests(ITestContext test) 
{  
    List<ITestNGMethod> failsToRemove = new ArrayList<ITestNGMethod>(); 
    IResultMap returnValue = test.getFailedTests(); 

    for(ITestResult result : test.getFailedTests().getAllResults()) 
    { 
    long failedResultTime = result.getEndMillis();   

    for(ITestResult resultToCheck : test.getSkippedTests().getAllResults()) 
    { 
     if(failedResultTime == resultToCheck.getEndMillis()) 
     { 
      failsToRemove.add(resultToCheck.getMethod()); 
      break; 
     } 
    } 

    for(ITestResult resultToCheck : test.getPassedTests().getAllResults()) 
    { 
     if(failedResultTime == resultToCheck.getEndMillis()) 
     { 
      failsToRemove.add(resultToCheck.getMethod()); 
      break; 
     } 
    }   
    } 

    for(ITestNGMethod method : failsToRemove) 
    { 
     returnValue.removeResult(method); 
    } 

    return returnValue; 
} 

Después de todo esto se hace, se puede añadir el indicador usando usando .addListener y especifique el retryAnalyzer en la anotación @Test.

+0

Muchísimas gracias, funciona muy bien, perfecto: D – user1000499

4

No necesita implementar onTestFailure. Las llamadas de TestNG vuelven a intentarse automáticamente cuando falla la prueba. Por lo tanto, no es necesario anular OnTestFailure. esto provoca el reintento del método 2 llamadas. Implementé el reintento de la siguiente manera.


private final Map rerunCountForTesCase = new HashMap(); 
    @Override 
    public boolean retry(ITestResult result) { 
       // here i have unique test case IDs for each of test method. 
       // So using utility method to get it. You can use method calss+name combination instead of testcaseid like me 
     String executingTestCaseID = ReportingUtilities.getTestCaseId(result); 
     if(rerunCountForTesCase.containsKey(executingTestCaseID)) 
     { 
      count = rerunCountForTesCase.get(executingTestCaseID); 
     } 
     else 
     { 
      rerunCountForTesCase.put(executingTestCaseID, 0); 
     } 
     if (count 0) 
       { 
        logInfo(tcID,"Sleeping for "+timeInSecs+ " secs before rerunning the testcase"); 
        Thread.sleep(timeInSecs * CommonFwBase.SHORTWAIT); 
       } 
      } catch (InterruptedException e) { 
       logError(null, e.getMessage()); 

      } 

      rerunCountForTesCase.put(executingTestCaseID, ++count); 
      return true; 
     } 
     return false; 

    } 

En el hilo anterior, vuelva a intentar llamar dos veces debido a la implementación de onTestFailure. Elimino los resultados fallidos para que cuando vuelva a intentarlo utilice el último resultado. De lo contrario, si tiene el método de prueba de dependencia, se saltará (aunque al volver a intentarlo pasará ya que utiliza el primer resultado). Es posible que tenga que gestionar resultados reintentados fallidos durante el informe. Es posible que tenga que eliminar las pruebas que están pasando después de intentarlo de este modo.

 m_ptests = suiteTestContext.getPassedTests(); 
     m_ftests = suiteTestContext.getFailedTests(); 
     m_stests = suiteTestContext.getSkippedTests(); 

     List<ITestNGMethod> methodsToRemove = new ArrayList<ITestNGMethod>(); 

     for(ITestResult failed_result : m_ftests.getAllResults()) 
     { 
      String failed_testName = failed_result.getMethod().getMethodName(); 
      String failingTest_className = failed_result.getClass().getName(); 
      for(ITestResult passed_result : m_ptests.getAllResults()) 
      { 
       String passing_testName = passed_result.getMethod().getMethodName(); 
       String passingTest_className = failed_result.getClass().getName(); 
       if(failed_testName.equals(passing_testName) && 
         passingTest_className.equals(failingTest_className))) 
       { 
        if(passed_result.getEndMillis() > failed_result.getEndMillis()) 
        { 

         methodsToRemove.add(failed_result.getMethod()); 
         break; 
        } 

       } 
      } 
     } 

     // remove the test that passed on retry 
     for(ITestNGMethod failedMethodToRemove : methodsToRemove) 
     { 
      m_ftests.removeResult(failedMethodToRemove); 
     } 

Espero que ayude a comprender reintentar mejor.

0

Las otras respuestas son la manera "correcta". Para una forma "rápida y sucia" simplemente mueva el código de prueba real a un método privado. En su método de prueba anotado, haga un for-loop con un intento ...atrapa adentro donde buscas Throwable (superclase de todos los errores y excepciones). En caso de error simplemente continúe con el ciclo o ejecute el último error, en el ciclo de interrupción de éxito.

Código de ejemplo:

@Test 
public void testA() throws Throwable { 
    Throwable err = null; 
    for (int i=0; i<4; i++) { 
     try { 
      testImplementationA(); 
      return; 
     } catch (Throwable e) { 
      err = e; 
      continue; 
     } 
    } 
    throw err; 
} 

private void testImplementationA() 
{ 
    // put test code here 
} 

Por lo general, sin embargo, es mejor escribir pruebas que no fallan al azar. Además, al usar este enfoque, no tiene información sobre cuántos intentos fallaron, que es una información importante después de todo. Personalmente, en lugar de eso, volví a ejecutar toda la clase/suite de prueba en Jenkins en caso de falla e investigo por qué falló.

Cuestiones relacionadas