TestNG重试失败的测试不会输出正确的测试结果

时间:2012-10-30 02:36:07

标签: testng

设置: 我有一个扩展IRetryAnalyzer的类,并实现了一个简单的重试逻辑,覆盖了以下方法:     public boolean retry(ITestResult result){

我有另一个类,它扩展了类TestListenerAdapter,它重试失败的测试,直到它们通过或报告失败。我已经实现了我的逻辑覆盖以下方法:     public void onTestFailure(ITestResult result){

情境: 我总共进行了10次测试。 10次​​测试中有1次失败2次,并使用我的重试逻辑传递第3次尝试。测试结果显示如下: 总测试:12,失败:2,跳过:0

我想要的是输出正确数量的测试。并且也忽略了自测试结束以来的2次失败。所以结果应该是这样的: 总测试:10,失败:0,跳过:0

我在这里缺少什么?我需要修改ITestResult对象吗?如果是,怎么样?

仅供参考:我能够使用JUnit实现这一点(实现TestRule接口)。

提前致谢。

5 个答案:

答案 0 :(得分:6)

请考虑以下测试结果。 2重试:

  1. Passed =>总的来说还好!
  2. 失败,通过=>总的来说还好!
  3. 失败,失败,通过=>总的来说还好!
  4. 失败,失败,失败=>整体失败!
  5. 我所做的是创建一个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)。这也意味着如果有几个失败的结果,我将只保留第一个失败的结果。
    3. 要识别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(name = "dataprovider")
      public Object[][] getData() {
          return new Object[][]{{System.currentTimeMillis()}};
      }
      

      对于失败的测试,重新评估数据提供者。因此,您将获得一个新的时间戳,同一个mehod的result1和result2将导致不同的哈希值。解决方案是在getId()方法中包含parameterIndex而不是参数,但似乎这样的值不包含在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 :(得分: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
 }

}

答案 2 :(得分:1)

我的方法建立在Morvader's answer之上,但增加了定义重试分析器的能力,即使在重试之后方法已经过去,也会遵循实际失败测试的原始意图。

我也没有发现需要倾向于onFinish()方法中的测试次数,这些数字在 maven-surefire-plugin version 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;
    }
}

答案 3 :(得分: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 - &gt; Surefire配置:

这是您应该配置&#34;覆盖&#34; surefire听众有自己的柜台。

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

答案 4 :(得分:0)

此问题已解决,无需在最新版本的TestNG 7.1.0中的Retry Analyzer中进行任何额外的处理。 现在,已重试的测试将被标记为“重试”,您可以在 运行后生成的emailable-report.html个报告。 如果您仍想在重试测试时进行一些逻辑处理,则可以执行以下操作:

@AfterMethod(alwaysRun = true)
    public void afterMethod( ITestResult result) throws Exception {
            if (result.wasRetried()){
                //your logic here
            }