2011-10-18 46 views
5

對於我的實習,我必須使用TestNG的硒用於測試的Web應用程序。但是我有一個問題,有時候硒或者瀏覽器因爲某些隨機原因而不工作,所以一個工作測試被標記爲「失敗」。爲了避免這種情況,我可以使用註釋@Test(invocationCount = 4, successPercentage = 25),那麼如果測試成功一次,測試被標記爲「成功」,這是好的,但問題是,這種解決方案乘以4的時間進行測試,這是不是很有效。如何優化TestNG的和seleniums測試

我能做些什麼來減少測試時間,就是寫一些規則「如果測試失敗,重新運行這個測試(並且只有在測試失敗的情況下),以及它是否工作了第二,第三或者第四次,然後將此測試標記爲「成功」「所以我可以避免這些隨機錯誤。但是我還沒有找到如何編寫這個規則,我看到我們可以添加一個監聽器,所以我們有一個名爲「onTestFailure」的方法,所以當測試失敗時我可以做一些事情,但是我不知道如何重新編寫代碼。運行測試。

我還發現了testng-failed.xml,其中保存了所有失敗的測試,所以我們可以運行這個xml文件來重新運行這些測試,但是這會從先前的第一次運行中刪除報告,但我只想如果第二次運行成功,失敗的測試將被標記爲「成功」。 (我已經將TestNG的/硒詹金斯,所以我有所有的測試圖形,所以這種方法不是很適應,但這種方法不乘以4進行測試的時候,這就是我想要的)

所以,如果你有任何線索,如何做到這一點,這將是非常好的。

+0

我已經測試過3次運行testng-failed.xml,然後所有的測試都可以正常工作,而且不需要很多時間。但對Jenkins來說,當testng-failed.xml最後一次運行時,它將編輯testng-result.xml,所以現在圖表顯示「1次測試運行,1次成功」,因爲上次運行testng僅啓動了這個測試第一次失敗了3次。 這種方法將產生一個圖表,所有的失敗測試,​​但所有的工作測試(除了第三次運行的測試)將不會被指出,這不完全是我想要的... 任何線索? – user1000499

回答

4

可以使用IRetryAnalyzer,監聽器和一個自定義的記者的組合,你在找什麼。

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

然而,似乎是內TestNG的錯誤,實際上導致了一些測試結果的報告爲兩跳過失敗。爲了防止這種情況,我建議你重寫你想使用和包括方法,如一個包含下面任何記者:

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

所有這一切都完成後,您可以使用.addListener使用add記者和在@Test註釋中指定retryAnalyzer。

+0

非常感謝,它效果很好,完美:D – user1000499

4

你不需要實現onTestFailure。 TestNG在測試失敗時會自動重試。所以不需要重寫onTestFailure。這會導致重試方法2調用。 我執行了如下重試。


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; 

    } 

在上面的線程中,因爲onTestFailure的實現而被重新調用兩次。我刪除失敗的結果,以便在重試時使用最新結果。否則,如果你有依賴測試方法,它會被跳過(儘管它在重試時會通過,因爲它使用第一個結果)。你可能不得不在處理報告時處理失敗的重試結果。 您可能必須刪除像這樣重試後傳遞的測試。

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

希望它有助於更​​好地理解重試。

0

其他的答案是「正確」的方式。對於「quick'n'dirty」方式,只需將實際測試代碼移至私有方法即可。在你的註釋測試方法中,用try ...做一個for-loop ..在裏面尋找Throwable(所有錯誤和例外的超類)。出錯時只是繼續循環或拋出最後一個錯誤,在成功中斷循環。

示例代碼:

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

通常,不過,最好是寫不隨機測試失敗。同樣使用這種方法,您沒有關於嘗試失敗次數的信息,畢竟這是一個重要的信息。我個人寧願在Jenkins失敗後重新運行整個測試課程/套件,並調查爲什麼失敗。