2011-10-18 4 views
5

Per il mio stage, devo utilizzare TestNG e selenio per testare un'applicazione web. Ma ho un problema, a volte il selenio o il browser non funziona per qualche motivo casuale, quindi un test di lavoro è contrassegnato come "fallito". Per evitare ciò, posso usare l'annotazione @Test(invocationCount = 4, successPercentage = 25), quindi se il test ha esito positivo una volta, il test è contrassegnato come "Successo", è buona, ma il problema è che questa soluzione moltiplica il tempo per il test di 4, questo non è molto efficiente .Come ottimizzare i test di test e selenio

Cosa posso fare per ridurre il tempo di test, è scrivere una regola "se il test non è riuscito, rieseguire questo test (e solo se il test ha fallito), e se ha funzionato il secondo, terzo o il quarta volta, quindi contrassegnare questo test come "successo" "Così posso evitare questi bug casuali. Ma non ho trovato come scrivere questa regola, ho visto che possiamo aggiungere un listener, quindi abbiamo un metodo chiamato "onTestFailure" così posso fare qualcosa quando il test ha fallito, ma non so come eseguire il test.

Ho trovato anche testng-failed.xml dove sono stati salvati tutti i test non riusciti, quindi è possibile eseguire questo file xml per rieseguire questi test, ma questo cancellerà il report dalla prima esecuzione precedente, ma voglio solo che il i test falliti sono contrassegnati come "riusciti" se la seconda esecuzione ha esito positivo. (Ho integrato testNG/selenio a Jenkins, quindi ho un grafico con tutti i test, quindi questo metodo non è molto adatto, ma questo metodo non moltiplica il tempo per il test di 4 e questo è quello che voglio)

Quindi, se hai qualche idea su come farlo, sarebbe molto bello.

+0

Ho testato per eseguire testng-failed.xml per 3 volte, quindi tutti i test sono funzionanti e non ci vuole molto tempo. Ma con Jenkins, quando il testng-failed.xml viene eseguito l'ultima volta, questo modificherà il testng-result.xml, quindi ora il grafico indica "1 test eseguito, 1 riuscito", perché l'ultima esecuzione, testng ha lanciato solo questo test che fallì le prime 3 volte. Questo metodo produrrà un grafico con tutti i test falliti, ma tutti i test di lavoro (eccetto i test eseguiti la terza volta) non saranno indicati, non è esattamente quello che voglio ... qualche indizio? – user1000499

risposta

4

È possibile utilizzare una combinazione di IRetryAnalyzer, un listener e un reporter personalizzato per fare ciò che si sta cercando.

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

Tuttavia, sembra che vi sia un bug all'interno di TestNG che provoca in realtà alcuni dei risultati dei test da segnalare come sia saltato e falliti. Per evitare questo, vi consiglio di ignorare tutto ciò Reporter che si desidera utilizzare e includono un metodo come quello riportato qui di seguito:

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

Dopo tutto questo è fatto, è possibile aggiungere il reporter utilizzando utilizzando .addListener e specificare retryAnalyzer nell'annotazione @Test.

+0

Un enorme grazie, funziona molto bene, perfetto: D – user1000499

4

Non è necessario implementare onTestFailure. Le chiamate TestNG riprovano automaticamente quando il test fallisce. Quindi non c'è bisogno di sovrascrivere su TestDail. ciò causa le chiamate del metodo 2 di ripetizione. Ho implementato un nuovo tentativo come di seguito.


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; 

    } 

Nel thread precedente riprovare a chiamare due volte a causa dell'implementazione di onTestFailure. Rimuovo i risultati non riusciti in modo che quando si riprova utilizza i risultati più recenti. In caso contrario, se si dispone del metodo di verifica delle dipendenze, viene saltato (anche se su retry viene passato in quanto utilizza il primo risultato). Potrebbe essere necessario gestire i risultati non riusciti durante la segnalazione. Potrebbe essere necessario rimuovere i test che stanno passando dopo un nuovo tentativo come questo.

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

Spero che aiuti a capire meglio i tentativi.

0

Le altre risposte sono la "giusta". Per un metodo "quick'n'dirty" basta spostare il codice di test effettivo su un metodo privato. Nel tuo metodo di prova annotato, crea un ciclo con un tentativo ..cattura dentro dove cerchi Throwable (superclasse di tutti gli errori e le eccezioni). In caso di errore è sufficiente continuare il ciclo o lanciare l'ultimo errore, nel ciclo di interruzione riuscita.

codice di esempio:

@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 
} 

Di solito, però, è meglio scrivere test che non mancano in modo casuale. Anche usando questo approccio non hai informazioni sul numero di tentativi falliti, che è un'informazione importante dopo tutto. Personalmente, invece, ho eseguito nuovamente l'intera classe di test/suite su Jenkins in caso di insuccesso, e ho indagato sul motivo del fallimento.