2011-10-18 11 views
5

Do mojego stażu, muszę użyć TestNG i selenu do testowania aplikacji internetowej. Ale mam problem, czasami selen lub przeglądarka nie działa z jakiegoś przypadkowego powodu, więc test roboczy jest oznaczony jako "nieudany". Aby tego uniknąć, mogę użyć adnotacji @Test(invocationCount = 4, successPercentage = 25), a jeśli test powiedzie się jeden raz, test jest oznaczony jako "Sukces", to dobrze, ale problem polega na tym, że to rozwiązanie zwielokrotnia czas testowania o 4, to nie jest bardzo wydajne .Jak zoptymalizować testy testng i selenów?

Co mogę zrobić, aby skrócić czas testowania, to napisać regułę: "jeśli test się nie powiódł, ponownie uruchom ten test (i tylko wtedy, gdy test się nie powiódł), a jeśli zadziałał drugi, trzeci lub Czwarty raz, a następnie oznacz ten test jako "sukces" "Tak więc mogę uniknąć tych losowych błędów. Ale nie znalazłem sposobu napisania tej reguły, widziałem, że możemy dodać słuchacza, więc mamy metodę o nazwie "onTestFailure", więc mogę coś zrobić, gdy test się nie powiedzie, ale nie wiem jak to zrobić uruchom test.

Znalazłem również testng-failed.xml, gdzie zapisano wszystkie nieudane testy, więc możemy uruchomić ten plik xml, aby ponownie uruchomić te testy, ale spowoduje to skasowanie raportu z poprzedniego pierwszego uruchomienia, ale chcę tylko, aby nieudane testy są oznaczone jako "pomyślnie", jeśli drugi bieg zakończył się pomyślnie. (Zintegrowałem testNG/selen z Jenkinsem, więc mam wykres ze wszystkimi testami, więc ta metoda nie jest bardzo dostosowana, ale ta metoda nie pomnoży czasu na testowanie przez 4 i to jest to czego chcę)

Więc jeśli masz jakieś wskazówki, jak to zrobić, byłoby bardzo miłe.

+0

Testowałem, aby uruchomić testng-failed.xml 3 razy, a następnie wszystkie testy działają, a to nie zajmuje dużo czasu. Ale w przypadku Jenkinsa, kiedy testng-failed.xml jest uruchamiany po raz ostatni, edytuje to plik testng-result.xml, więc teraz wykres wskazuje "1 przebieg testowy, 1 sukces", ponieważ w ostatnim uruchomieniu testng uruchomił tylko ten test zakończył się niepowodzeniem 3 razy. Ta metoda spowoduje wygenerowanie wykresu z nieudanym testem, ale wszystkie testy robocze (z wyjątkiem testów przeprowadzanych po raz trzeci) nie będą wyświetlane, nie jest to dokładnie to, czego chcę ... jakaś wskazówka? – user1000499

Odpowiedz

4

Możesz użyć kombinacji IRetryAnalyzer, słuchacza i niestandardowego reportera, aby zrobić to, czego szukasz.

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

Jednak wydaje się, że to błąd w ciągu TestNG że faktycznie powoduje, że część wyników badań należy podać zarówno jako pominięty i nie powiodła się,. Aby tego uniknąć, polecam, aby zastąpić cokolwiek Reporter chcesz używać i obejmują metody takie jak jeden zawarte poniżej:

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

Mimo wszystko to odbywa, można dodać reporter użyciu stosując .addListener i określ parametr retryAnalyzer w adnotacji @Test.

+0

Ogromne dzięki, działa bardzo dobrze, idealnie: D – user1000499

4

Nie musisz wdrażać onTestFailure. Połączenia TestNG są ponawiane automatycznie po niepowodzeniu testu. Więc nie ma potrzeby, aby zastąpić onTestFailure. powoduje to wywołanie metody 2 metody. Zaimplementowałem ponów jak poniżej.


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; 

    } 

W powyższym wątku ponawiam próby wywołania dwa razy z powodu wdrożenia onTestFailure. Usuwam nieudane wyniki, aby po ponownym użyciu używał najnowszego wyniku. W przeciwnym razie, jeśli masz metodę testu zależności, to zostanie ona pominięta (chociaż po ponownej próbie jest przekazywana, ponieważ wykorzystuje pierwszy wynik). Podczas raportowania może być konieczne przetwarzanie wyników nie powiodło się. Może być konieczne usunięcie testów, które przechodzą po ponowieniu w ten sposób.

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

Mam nadzieję, że pomoże to lepiej spróbować ponownie.

0

Pozostałe odpowiedzi to "właściwy" sposób. Dla "szybkiego" i "złego" sposobu wystarczy przenieść rzeczywisty kod testowy do metody prywatnej. W swojej opisanej metodzie testowania wykonaj pętlę for z próbą.złap wewnątrz, gdzie szukasz Throwable (superklasa wszystkich błędów i wyjątków). Przy błędzie po prostu kontynuuj pętlę lub wyrzuć ostatni błąd w pętli break success.

Przykładowy kod:

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

Zazwyczaj jednak, że lepiej napisać testy, które nie zawodzą losowo. Również przy użyciu tego podejścia nie masz informacji o tym, ile prób zakończyło się niepowodzeniem, co jest istotną informacją. Osobiście raczej ponownie przetestuję całą testową klasę/pakiet na Jenkins po niepowodzeniu i zbadam, dlaczego się nie udało.