2

I am working on a testng project and my goal is that let testng auto retry the failed test cases. For example, there are 10 test cases in the first round and 5 failed. So after first round, I let testng select the 5 failed test cases and rerun them again. In the second round, maybe there are 2 failed test cases, then I rerun this 2 agian.

I have tried the IRetryAnalyzer but it is different. The IRetryAnalyzer is retrying the failed test cases immediatelly instead of the end of each round.

So currently I want to call the retry using onStart and onFinish in the ISuiteListener. In this case I implement the onFinish method like this:

@Override
public void onFinish(ISuite suite) {
    logger.info("Round " + retryCounter
            + " Testing suit stops. onFinish method is invoked.");
    if (!doRetry()) {
        logger.info("Retry finished.");
        cleanReport(suite);
        return;
    }

    // I want to remove the passed cases here
    // and create a suite to run the failed test cases.
    suite.run();
}

So is it possible do that? Or any better idea for this requirement.

Ciel
  • 5,551
  • 5
  • 17
  • 24

3 Answers3

2

It looks like the question is old, but still unanswered, so here is my solution.

Firstly, you have to implement IRetryAnalyzer interface like this:

    public class Retry implements IRetryAnalyzer {

    private int retryCount = 0;
    private int maxRetryCount = 1;

    public boolean retry(ITestResult result) {
        if (retryCount < maxRetryCount) {
            System.out.println("Retrying test " + result.getName() + " with status "
                    + getResultStatusName(result.getStatus()) + " for the " + (retryCount+1) + " time(s).");
            retryCount++;
            return true;
        }
        return false;
    }

    public String getResultStatusName(int status) {
        String resultName = null;
        if(status==1)
            resultName = "SUCCESS";
        if(status==2)
            resultName = "FAILURE";
        if(status==3)
            resultName = "SKIP";
        return resultName;
    }
}

Here, maxRetryCount is the number of times, you want your failed tests to be re-run. Tests are re-run immediately after their fail.

Secondly, implement IAnnotationTransformer interface:

public class RetryListener implements IAnnotationTransformer {

    @Override
    public void transform(ITestAnnotation testannotation, Class testClass,
                          Constructor testConstructor, Method testMethod)   {
        IRetryAnalyzer retry = testannotation.getRetryAnalyzer();

        if (retry == null)  {
            testannotation.setRetryAnalyzer(Retry.class);
        }

    }
}

Now, in your TestNG xml suite with tests, you should add listener:

<listeners>
    <listener class-name="Utils.reRunTest.RetryListener"/>
</listeners>

You can also add it to your pom.xml file:

 <properties>
     <property>
         <name>usedefaultlisteners</name>
         <value>false</value>
     </property>
     <property>
         <name>listener</name>
         <value>org.testng.reporters.EmailableReporter2, org.testng.reporters.XMLReporter, org.testng.reporters.FailedReporter, Utils.reRunTest.RetryListener
         </value>
     </property>

You may also want your test re-runs not to affect total number of tests. This is done, by implementing TestListener interface and adding it as a listener alongside with RetryAnalyzer. The implementation that will remove flaky tests and failed tests from total number of tests is:

public class TestListener implements ITestListener {
    @Override
    public void onFinish(ITestContext context) {
        Set<ITestResult> failedTests = context.getFailedTests().getAllResults();
        Set<ITestResult> skippedTests = context.getSkippedTests().getAllResults();
        for (ITestResult temp : failedTests) {
            ITestNGMethod method = temp.getMethod();
            if (context.getFailedTests().getResults(method).size() > 1) {
                failedTests.remove(temp);
            } else {
                if (context.getPassedTests().getResults(method).size() > 0) {
                    failedTests.remove(temp);
                }
            }
        }
        for (ITestResult temp : skippedTests) {
            ITestNGMethod method = temp.getMethod();
            if (context.getSkippedTests().getResults(method).size() > 1) {
                skippedTests.remove(temp);
            } else {
                if (context.getPassedTests().getResults(method).size() > 0) {
                    skippedTests.remove(temp);
                }
            }
        }
    }

    public void onTestStart(ITestResult result) {
    }

    public void onTestSuccess(ITestResult result) {
    }

    public void onTestFailure(ITestResult result) {
    }

    public void onTestSkipped(ITestResult result) {
    }

    public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
    }

    public void onStart(ITestContext context) {
    }
}

Hope that helps. P.S Don't use it with TestNG version 6.9.10 `cause it has some issues and you re-run tests will always pass, disregarding their real state.

Remy
  • 31
  • 6
  • This does not seem to be working if i try to run the same set of tests multiple times in different threads/same threads. ` ` Where i have 3 failed tests in ParallelTest class. In this case test with name="Name1" failed tests are re-running but not name="Name2" failed tests. – user2649233 Nov 15 '16 at 12:35
  • This is excellent. Thanks for taking the time to share your findings! – SkipKent Dec 15 '16 at 17:06
0

Testng creates a testng-failed.xml in the output folder after a run, which you can run after running the testng.xml. So you have the 10 testcases in testng.xml and run them and whatever fails will come in the testng-failed.xml which you can run then.

Shamik
  • 1,591
  • 2
  • 16
  • 36
  • I know this, but thing is that I have to rerun them manually. I want the rerun could be done automatically and to provide a way to configure the rerun counter. – Ciel Jan 30 '15 at 02:01
  • This can be done automatically. Depends on how you launch the run . I used ant so there i can create task for running testng and once that is over send the reports and then run another task to run the failed-testng xml. – Shamik Jan 30 '15 at 11:16
  • Thanks Shamik. We team uses maven and we also find some way to trigger the rerun with maven task. But as I mentioned, we need a clear report - we have thousands of test cases - in that case, we want to merge reports together. But if we trigger the rerun from maven or ant task, then we will have two reports, one for the first round and one for the failed-testng.xml – Ciel Feb 02 '15 at 03:11
  • Yes. that will create two separate reports. – Shamik Feb 02 '15 at 08:06
  • We implemented this by running two gradle tasks first with normal tests execution and second with only failing tests (using testng-failed.xml) . Once both tasks are done, we combine generated testng-results.xml file & merge it to make consolidated xml file . Merging xml is using logic like (if test is failed in first run but passed in second run then mark test as pass like that) . – sjethvani Oct 24 '18 at 07:33
0

If testng version is 7.4.0 or more then retry test cases are getting total count and it is also present skip section as well.

To solve this problem we need to do a little code change in TestListener class.

The following code snippet worked for me.

public class RetryAnalyzer extends RetryAnalyzerCount
{

    @Override
    public boolean retryMethod(ITestResult result)
    {
        int count = 0;
        int trackingCount = 0;
        Properties prop = new Properties();
        String dir = null;
        FileInputStream input = null;
        
        try
        {
            //Reading Property file to get Retry Count.
            dir = new File(System.getProperty("user.dir")).getParent();
            dir = dir + "Your Properties Path";
            input = new FileInputStream(dir);
            prop.load(input);
        }
        catch (FileNotFoundException e)
        {
            count = 1;
            e.printStackTrace();
        }
        catch (IOException e)
        {
            e.printStackTrace();
        }
        count = Integer.parseInt(prop.getProperty("RetryCount"));
        
        if (!result.isSuccess())
        {
            while (count != 0)
            {
                count--;
                trackingCount++;
                result.setStatus(ITestResult.SUCCESS_PERCENTAGE_FAILURE);
                String message = Thread.currentThread().getName() + "Error in '" + result.getName() + "' with status '" + result.getStatus() + "'. Retrying '" + trackingCount + "' times.";
                Reporter.log(message, true);
                return true;
            }
        }
        return false;
        
    }
}

In this we are maintaining thre retry count in a property file so that we can change the retry count without compiling the code.

Below is values of properties file.

RetryCount=1

After that we need to change TestListener class.

    @Override
    public void onFinish(ITestContext context)
    {
        //extent.flush();
        Set<ITestResult> failedTests = context.getFailedTests().getAllResults();
        Set<ITestResult> skippedTests = context.getSkippedTests().getAllResults();
        Set<ITestResult> passedTests = context.getPassedTests().getAllResults();
        for (ITestResult temp : failedTests) {
            ITestNGMethod method = temp.getMethod();
            System.out.println("Method Name  " + method.getMethodName() + "  Have Result " + temp.getStatus() + "  Have Retries ?  " +temp.wasRetried() + " \n " + " have result " +context.getFailedTests().getResults(method).toString());
            if (temp.wasRetried()){
                failedTests.remove(temp);
            }
        }
        for (ITestResult temp : skippedTests) {
            ITestNGMethod method = temp.getMethod();
            System.out.println("Method Name  " + method.getMethodName() + "  Have Result " + temp.getStatus() + "   Have Retries ?  " +temp.wasRetried() + " \n " + " Have Result " + context.getSkippedTests().getResults(method).toString());
            if (temp.wasRetried()){
                skippedTests.remove(temp);
            }
        }
        for (ITestResult temp : passedTests) {
            ITestNGMethod method = temp.getMethod();
            System.out.println("Method Name  " + method.getMethodName() + "  Have Result " + temp.getStatus() + "   Have Retries ?  " +temp.wasRetried() + " \n " + " Have Result " + context.getPassedTests().getResults(method).toString());
            if (temp.wasRetried()){
                passedTests.remove(temp);
            }
        }
   }

It will remove the retry test cases count from skip section.

Now if you retrying the testcases at testcase level the only need to add

@Test(retryAnalyzer=RetryAnalyzer.class,timeOut = DEFAULT_TEST_TIMEOUT, description = "XXX", dataProvider = "YYY", groups ={"ZZZ"})
    public void test_A() {

    }

But if You are retry testcase at suite level then need to create another class

public class AnnotationTransformer implements IAnnotationTransformer {

    @Override
    public void transform(ITestAnnotation annotation, Class testClass,Constructor testConstructor ,Method testMethod)
    {
        annotation.setRetryAnalyzer(RetryAnalyzer.class);
    }
}

And then call it in testng.xml file.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
    <suite name="RetryFailedTests" verbose="1" preserve-order="true" parallel="methods" thread-count="1" >
  <listeners>
        <listener class-name="AnnotationTransformer"/>
  </listeners>
  <test name="RetryMulitple">
        <classes> 
            <class name="Your TestCase"></class>   
        </classes>
    </test>
 </suite>
mauryaAjay
  • 249
  • 2
  • 9