2011-10-18 5 views
5

私のインターンシップでは、WebアプリケーションのテストにTestNGとSeleniumを使用する必要があります。しかし、私は問題があります。時にはセレンやブラウザが何らかのランダムな理由で動作していないため、動作テストに「失敗」と表示されます。それを避けるために、私は@Test(invocationCount = 4, successPercentage = 25)という注釈を使用することができます。テストが成功すると "成功"とマークされますが、問題はこのソリューションがテスト時間を4倍にするという問題です。これはあまり効率的ではありません。testngとseleniumsテストを最適化する方法

テスト時間を短縮するには、「テストが失敗した場合はこのテストを再実行します(テストが失敗した場合のみ)」というルールを書き、2番目、3番目、またはこのテストを「成功」とマークしてください。これらのバグを避けることができます。しかし、私はこのルールを書く方法を見つけられませんでした。私はリスナーを追加できることを知ったので、 "onTestFailure"というメソッドがあります。テストが失敗したときに何かできますが、テストを実行します。

また、失敗したテストがすべて保存されているtestng-failed.xmlもあります。このxmlファイルを実行してこれらのテストを再実行することができますが、前回の実行からレポートが消去されます。失敗したテストは、2回目の実行が成功すると「成功」とマークされます。 (私はJenkinsにtestNG/seleniumを統合しているので、すべてのテストのグラフがあるので、この方法はあまり適応していませんが、この方法ではテスト時間が4倍にならず、これが私の望むものです)

それを行う方法の手がかりがあるなら、それはとても良いでしょう。

+0

私はtestng-failed.xmlを3回実行することをテストしています。その後、すべてのテストが機能しており、時間がかかりません。しかしJenkinsでは、testng-failed.xmlが最後に実行されたときにtestng-result.xmlを編集するので、グラフは「1回のテスト実行、1回の成功」を示します。 3回目のテストに失敗したこのテスト。 この方法では、すべての失敗したテストを含むグラフが生成されますが、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を使用して使用してレポーターを追加することができますし、 @TestアノテーションにretryAnalyzerを指定します。

+0

巨大なあなたに感謝、それは完璧に、非常にうまく動作します:D – user1000499

4

onTestFailureを実装する必要はありません。テストが失敗すると、TestNGは自動的に再試行を行います。したがって、onTestFailureをオーバーライドする必要はありません。これにより、メソッド2のコールを再試行します。 次のように再試行しました。なぜならonTestFailureの実装の二倍と呼ばなっ上記スレッドの再試行で


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; 

    } 

。失敗した結果を削除して、再試行すると最新の結果が使用されます。それ以外の場合は、依存関係テストメソッドがあるとスキップされます(ただし、最初の結果が使用されるため、再試行で渡されます)。レポート中に失敗した再試行結果を処理する必要があります。 このように再試行した後に通過するテストを削除する必要があります。

 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」の方法では、実際のテストコードをプライベートメソッドに移動するだけです。あなたの注釈付きテスト方法で、試しに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のテストクラス/スイート全体を個人的に再実行し、失敗した理由を調べます。

関連する問題