2011-10-18 5 views
5

Pour mon stage, je dois utiliser TestNG et le sélénium pour tester une application web. Mais j'ai un problème, parfois sélénium ou le navigateur ne fonctionne pas pour une raison quelconque, donc un test de travail est marqué comme "échoué". Pour éviter cela, je peux utiliser l'annotation @Test(invocationCount = 4, successPercentage = 25), puis si le test réussit une fois, le test est marqué comme "Réussir", c'est bien mais le problème est que cette solution multiplie le temps de test par 4, ce n'est pas très efficace . Ce que je peux faire pour réduire le temps de test, c'est d'écrire une règle "si le test échoue, relancez ce test (et seulement si le test a échoué), et si cela a fonctionné le deuxième, le troisième ou le quatrième fois, puis marquer ce test comme "réussir" "Donc, je peux éviter ces bogues aléatoires. Mais je n'ai pas trouvé comment écrire cette règle, j'ai vu que nous pouvions ajouter un écouteur, donc nous avons une méthode appelée "onTestFailure" donc je peux faire quelque chose quand le test a échoué mais je ne sais pas comment exécuter le test. J'ai également trouvé testng-failed.xml où tous les tests échoués sont enregistrés, donc nous pouvons exécuter ce fichier xml pour relancer ces tests, mais cela effacera le rapport de la première exécution précédente, mais je veux juste que le les tests échoués sont marqués comme "réussir" si la deuxième exécution réussit. (J'ai intégré testNG/sélénium à Jenkins, donc j'ai un graphique avec tous les tests, donc cette méthode n'est pas très adaptée, mais cette méthode ne multiplie pas le temps pour tester par 4 et c'est ce que je veux)Comment optimiser les tests de test et de sélénium

Donc, si vous avez la moindre idée de comment faire cela, ce serait très bien.

+0

J'ai testé 3 fois le testng-failed.xml, puis tous les tests fonctionnent, et cela ne prend pas beaucoup de temps. Mais avec Jenkins, quand testng-failed.xml est exécuté la dernière fois, cela modifiera le testng-result.xml, alors maintenant le graphique indique "1 test run, 1 réussir", parce que la dernière exécution, testng a été lancée seulement ce test qui a échoué les 3 premières fois. Cette méthode produira un graphique avec tout le test échoué, mais tous les tests de travail (sauf les tests qui sont exécutés la troisième fois) ne seront pas indiqués, ce n'est pas exactement ce que je veux ... aucune idée? – user1000499

Répondre

4

Vous pouvez utiliser une combinaison de IRetryAnalyzer, d'un écouteur et d'un rapporteur personnalisé pour faire ce que vous recherchez.

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

Cependant, il semble y avoir un bug dans les TestNG qui provoque en fait quelques-uns des résultats des tests à signaler à la fois sautée et a échoué. Pour éviter cela, je vous recommande de passer outre ce que Reporter que vous souhaitez utiliser et comprendre un procédé tel que celui présenté ci-dessous:

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

Après tout cela est fait, vous pouvez ajouter le journaliste à l'aide en utilisant .addListener et spécifiez le paramètre retryAnalyzer dans l'annotation @Test.

+0

Un grand merci, ça marche très bien, parfait: D – user1000499

4

Vous n'avez pas besoin d'implémenter onTestFailure. Les appels TestNG réessayent automatiquement lorsque le test échoue. Donc pas besoin de surcharger onTestFailure. cela provoque des appels de méthode 2. J'ai implémenté réessayer comme ci-dessous.


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; 

    } 

Dans le thread ci-dessus réessayer d'être appelé deux fois en raison de l'implémentation de onTestFailure. Je supprime les résultats échoués de sorte que lorsque vous réessayez, il utilise le dernier résultat. Sinon, si vous avez une méthode de test de dépendance, elle est ignorée (mais si elle est réessayée, elle est transmise car elle utilise le premier résultat). Vous devrez peut-être gérer les nouveaux résultats échoués lors du reporting. Vous devrez peut-être supprimer les tests qui passent après une nouvelle tentative comme celle-ci.

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

Espérons que cela aide à mieux comprendre.

0

Les autres réponses sont la "bonne" façon. Pour un "quick'n'dirty", déplacez simplement le code du test vers une méthode privée. Dans votre méthode de test annotée, faites une boucle for avec un essai.attraper à l'intérieur où vous cherchez Throwable (superclasse de toutes les erreurs et exceptions). En cas d'erreur, continuez la boucle ou lancez la dernière erreur, en cas de succès break.

Exemple de code:

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

En général, cependant, il est préférable d'écrire des tests qui ne manquent pas au hasard. En utilisant également cette approche, vous n'avez pas d'informations sur le nombre de tentatives échouées, ce qui est une information importante après tout. Personnellement, je préfère relancer l'ensemble de la classe de test/suite sur Jenkins en cas d'échec, et d'enquêter sur les raisons de l'échec.

Questions connexes