2012-10-30 98 views
12

設置: 我具有延伸的IRetryAnalyzer並已實現了一個簡單的重試邏輯重寫以下方法的類: 公共布爾重試(ITestResult結果){TestNG的重試失敗的測試不輸出正確的測試結果

我有另一個類擴展了TestListenerAdapter類,它重試失敗的測試,直到它們傳遞或報告失敗爲止。我已經實現我的邏輯覆蓋下面的方法: 公共無效onTestFailure(ITestResult結果){

方案: 我具有總共10次測試。 10次​​測試中有1次失敗2次,並通過重試邏輯傳遞第3次嘗試。測試結果顯示如下: 總測試:12,失敗:2,跳過:0

我想輸出正確數量的測試運行。並且也不考慮自從測試在最後通過以來的兩次失敗。所以結果應該是這樣的: 總測試:10,失敗:0,跳過:0

我在這裏丟失了什麼?我需要修改ITestResult對象嗎?如果是,如何?

僅供參考:我能夠使用JUnit(實現TestRule接口)實現此目的。

在此先感謝。

+1

你找到一個解決的辦法了嗎?謝謝。 – derrdji

+0

不幸的不是。現在就解決問題。 –

回答

5

請考慮以下測試結果與最大。 2次重試:

  1. 通過=>總的來說還可以!
  2. 失敗,已通過=>總體可以!
  3. 失敗,失敗,通過=>總體OK!
  4. 失敗,失敗,失敗=>總體失敗!

我所做的是創建一個TestNg偵聽器,它擴展了默認行爲,並在所有測試完成後執行一些魔術。

public class FixRetryListener extends TestListenerAdapter { 

    @Override 
    public void onFinish(ITestContext testContext) { 
     super.onFinish(testContext); 

     // List of test results which we will delete later 
     List<ITestResult> testsToBeRemoved = new ArrayList<>(); 

     // collect all id's from passed test 
     Set <Integer> passedTestIds = new HashSet<>(); 
     for (ITestResult passedTest : testContext.getPassedTests().getAllResults()) { 
      passedTestIds.add(TestUtil.getId(passedTest)); 
     } 

     Set <Integer> failedTestIds = new HashSet<>(); 
     for (ITestResult failedTest : testContext.getFailedTests().getAllResults()) { 

      // id = class + method + dataprovider 
      int failedTestId = TestUtil.getId(failedTest); 

      // if we saw this test as a failed test before we mark as to be deleted 
      // or delete this failed test if there is at least one passed version 
      if (failedTestIds.contains(failedTestId) || passedTestIds.contains(failedTestId)) { 
       testsToBeRemoved.add(failedTest); 
      } else { 
       failedTestIds.add(failedTestId); 
      } 
     } 

     // finally delete all tests that are marked 
     for (Iterator<ITestResult> iterator = testContext.getFailedTests().getAllResults().iterator(); iterator.hasNext();) { 
      ITestResult testResult = iterator.next(); 
      if (testsToBeRemoved.contains(testResult)) { 
       iterator.remove(); 
      } 
     } 

    } 

} 

基本上我做兩件事情:

  1. 收集全部通過測試。如果我遇到至少有一個通過測試失敗的測試,我將刪除失敗的測試(這將覆蓋上面的情況2和3)
  2. 迭代所有失敗的測試。如果我遇到以前失敗的失敗測試,​​請刪除當前失敗的結果。 (這實際上將包括案例3和4)。這也意味着如果有幾個失敗的結果,我將只保留第一個失敗的結果。

要確定一個TestResult我用下面簡單的哈希函數:

public class TestUtil { 

    public static int getId(ITestResult result) { 
     int id = result.getTestClass().getName().hashCode(); 
     id = 31 * id + result.getMethod().getMethodName().hashCode(); 
     id = 31 * id + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0); 
     return id; 
    } 
} 

這種方法並用的dataProvider工作很好,但有一個小的限制!如果您使用的dataProvider使用隨機值你會碰到的問題:

@DataProvider(name = "dataprovider") 
public Object[][] getData() { 
    return new Object[][]{{System.currentTimeMillis()}}; 
} 

對於失敗的測試的dataProvider是重新評估。因此,您將得到一個新的時間戳,同一個mehod的result1和result2將導致不同的散列值。解決方法是將參數指數包含在getId()方法中而不是參數中,但似乎這樣的值不包含在ITestResult中。

看到這個簡單的例子作爲概念驗證:

@Listeners(value = FixRetryListener.class) 
public class SimpleTest { 

    private int count = 0; 

    @DataProvider(name = "dataprovider") 
    public Object[][] getData() { 
     return new Object[][]{{"Run1"},{"Run2"}}; 
    } 

    @Test(retryAnalyzer = RetryAnalyzer.class, dataProvider = "dataprovider") 
    public void teste(String testName) { 
     count++; 
     System.out.println("---------------------------------------"); 
     System.out.println(testName + " " + count); 
     if (count % 3 != 0) { 
      Assert.fail(); 
     } 

     count = 0; 
    } 

} 

將產生在:

Total tests run: 2, Failures: 0, Skips: 0 
+1

當我嘗試這樣做時,它確實檢測到要正確刪除哪些測試(即在迭代器循環中打印出正確的測試)。但是,當我運行它時,仍然會「完成3個測試,2個失敗」。它似乎並沒有真正被刪除的結果。你之前遇到過這種情況嗎?我使用gradle來執行測試。 – linguinee

+1

我們遇到同樣的問題,測試似乎被刪除,但結果保持不變。我甚至試過這個,而不是iterator.remove():testContext.getFailedTests()。removeResult(testResult); – mac

+1

我其實也注意到以下幾點:「測試結果」和「套件結果」之間存在差異。套件是正確的(「默認套件 - 總測試運行:2,失敗:1,跳過:0」) - 一個失敗的測試被刪除。但是,測試不正常(「默認測試 - 測試運行:3,失敗:2,跳過:0」) - 總共還有3次測試,還有兩次失敗。 – mac

0

我用這個辦法:

ListenerApadter

public class MyTestListenerAdapter extends TestListenerAdapter { 
    @Override 
    public void onTestFailure(ITestResult result) { 
     if (result.getMethod().getRetryAnalyzer() != null) { 
      MyRetryAnalyzer retryAnalyzer = (MyRetryAnalyzer)result.getMethod().getRetryAnalyzer(); 

      if(retryAnalyzer.isRetryAvailable()) { 
       result.setStatus(ITestResult.SKIP); 
      } else { 
       result.setStatus(ITestResult.FAILURE); 
      } 
      Reporter.setCurrentTestResult(result); 
     } 
    } 

    @Overrride 
    public void onFinish(ITestContext context) { 
    Iterator<ITestResult> failedTestCases =context.getFailedTests().getAllResults().iterator(); 
    while (failedTestCases.hasNext()) { 
     System.out.println("failedTestCases"); 
     ITestResult failedTestCase = failedTestCases.next(); 
     ITestNGMethod method = failedTestCase.getMethod(); 
     if (context.getFailedTests().getResults(method).size() > 1) { 
      System.out.println("failed test case remove as dup:" + failedTestCase.getTestClass().toString()); 
      failedTestCases.remove(); 
     } else { 

      if (context.getPassedTests().getResults(method).size() > 0) { 
       System.out.println("failed test case remove as pass retry:" + failedTestCase.getTestClass().toString()); 
       failedTestCases.remove(); 
      } 
     } 
    } 
    } 
} 

RetryAnalizer

public class MyRetryAnalyzer implements IRetryAnalyzer { 
    private static int MAX_RETRY_COUNT = 3; 

    AtomicInteger count = new AtomicInteger(MAX_RETRY_COUNT); 

    public boolean isRetryAvailable() { 
     return (count.intValue() > 0); 
    } 

    @Override 
    public boolean retry(ITestResult result) { 
     boolean retry = false; 
     if (isRetryAvailable()) { 
      System.out.println("Going to retry test case: " + result.getMethod() + ", " + (MAX_RETRY_COUNT - count.intValue() + 1) + " out of " + MAX_RETRY_COUNT); 
      retry = true; 
      count.decrementAndGet(); 
     } 
     return retry; 
    } 
} 

的pom.xml - >神火配置:

這是你應該配置 「覆蓋」 萬無一失聽衆至極擁有自己的專櫃。

<plugin> 
    <groupId>org.apache.maven.plugins</groupId> 
    <artifactId>maven-surefire-plugin</artifactId> 
    <version>2.18.1</version> 
    <configuration> 
    <suiteXmlFiles><suiteXmlFile>${basedir}/testng.xml</suiteXmlFile></suiteXmlFiles> 
<properties> 
    <property> 
    <name>listener</name> 
    <value>Utils.MyTestListenerAdapter,Utils.MyRetryAnalizer</value> 
    </property> 
</properties> 

1

我的方法建立在Morvader's answer但增加了定義堅持以實際失敗即使該方法有一定試之後通過測試的原意重試分析的能力。

我還沒有找到一個需要傾向於測試在onFinish()方法的數量,號碼 Maven的萬無一失插件版本似乎罰款2.18

RetryListenerAdapter

public class RetryListenerAdapter extends TestListenerAdapter { 

    @Override 
    public void onTestFailure(ITestResult tr) { 
     IRetryAnalyzer retryAnalyzer = tr.getMethod().getRetryAnalyzer(); 
     if (retryAnalyzer == null || !(retryAnalyzer instanceof IRetryAnalyzerWithSkip)) { 
      super.onTestFailure(tr); 
     } else if (((IRetryAnalyzerWithSkip) retryAnalyzer).isRetryable()) { 
      tr.setStatus(ITestResult.SKIP); 
      super.onTestSkipped(tr); 
     } else { 
      super.onTestFailure(tr); 
     } 
    } 
} 

IRetryAnalyzerWithSkip

public interface IRetryAnalyzerWithSkip extends IRetryAnalyzer { 
    boolean isRetryable(); 
} 

重試

public class Retry implements IRetryAnalyzerWithSkip { 
    private int retryCount = 0; 
    private int maxRetryCount = 3; 

    public boolean retry(ITestResult result) { 

     if (retryCount < maxRetryCount) { 
      retryCount++; 
      return true; 
     } 
     return false; 
    } 

    @Override 
    public boolean isRetryable() { 
     return retryCount < maxRetryCount; 
    } 
} 
2

我試過了,試了又試。 但現在終於它的工作

MyRetryAnalyzer.java

import org.testng.IRetryAnalyzer; 
import org.testng.ITestResult; 

import java.util.concurrent.atomic.AtomicInteger; 


public class MyRetryAnalyzer implements IRetryAnalyzer { 
private static int MAX_RETRY_COUNT = 3; 

AtomicInteger count = new AtomicInteger(MAX_RETRY_COUNT); 

public boolean isRetryAvailable() { 
    return (count.intValue() > 0); 
} 

@Override 
public boolean retry(ITestResult result) { 
    boolean retry = false; 
    if (isRetryAvailable()) { 
     System.out.println("Going to retry test case: " + result.getMethod() + ", " + (MAX_RETRY_COUNT - count.intValue() + 1) + " out of " + MAX_RETRY_COUNT); 
     retry = true; 
     count.decrementAndGet(); 
    } 
    return retry; 
} 
} 

MyTestListenerAdapter。java的

import org.testng.*; 

import java.util.*; 


public class MyTestListenerAdapter extends TestListenerAdapter { 
    @Override 
    public void onTestFailure(ITestResult result) { 
     if (result.getMethod().getRetryAnalyzer() != null) { 
      MyRetryAnalyzer retryAnalyzer = (MyRetryAnalyzer)result.getMethod().getRetryAnalyzer(); 

      if(retryAnalyzer.isRetryAvailable()) { 
      } else { 
       result.setStatus(ITestResult.FAILURE); 
      } 
      Reporter.setCurrentTestResult(result); 
     } 
    } 

@Override 
    public void onFinish(ITestContext context) { 
     Iterator<ITestResult> failedTestCases =context.getFailedTests().getAllResults().iterator(); 
     while (failedTestCases.hasNext()) { 
      System.out.println("failedTestCases"); 
      ITestResult failedTestCase = failedTestCases.next(); 
      ITestNGMethod method = failedTestCase.getMethod(); 
      if (context.getFailedTests().getResults(method).size() > 1) { 
       System.out.println("failed test case remove as dup:" + failedTestCase.getTestClass().toString()); 
       failedTestCases.remove(); 
      } else { 

       if (context.getPassedTests().getResults(method).size() > 0) { 
        System.out.println("failed test case remove as pass retry:" + failedTestCase.getTestClass().toString()); 
        failedTestCases.remove(); 
       } 
      } 
     } 
    } 
} 

您的測試類

@Listeners(value = MyTestListenerAdapter.class) 

public class Test { 

//Your data provider 
@DataProvider 

@Test(retryAnalyzer = MyRetryAnalyzer.class) 
public void testMethod() { 
    //your code goes here 
} 

}