Retry executing only Failed Tests using TestNG

transferred from

http://www.cnblogs.com/alterhu/p/3191701.html 

Problem situation                                                 

Let's talk about the problem first. Recently, I am doing automated testing by integrating testNG and selenium.

Because if testNG is used for UI testing, the test may fail in many cases, but these failures may be caused by some other problems, possibly caused by script problems or unstable network environment, so we need to retry to run This failed test case.

testNG does not have a direct retry testcase function, but it provides a lot of interfaces, which we can implement to get the effect of retry.

On google, I saw that Taobao's QA project team used the Ruby language to modify the retry function of the source code of testNG, and then rebuilt it. This is a solution, but I don't recommend it. There are two reasons:

1. The modified jar package is for the specified testNG version, so if we need to experience the new version function of testNG, this jar may need to be basically rebuilt in the source code, which is a bit inappropriate. The detailed address is: https://github .com/NetEase/Dagger/wiki/Retry-Failed-Or-Skipped-Testcases

2. This modified method can only be used at the testcase level. If you need to use this feature for all testNG testsuites, you may need each testcase to indicate that they use this retry function, which is a bit redundant. Declare the retry class in testcase like this:

import org.apache.log4j.Logger;
import org.testng.Assert;
import org.testng.annotations.Test;

import com.hp.baserunner.RetryFail;
import com.hp.pop.DemoPage;

public  class DemoRun {

    private  static Logger log = Logger.getLogger (DemoRun. class );
    @Test(retryAnalyzer=RetryFail. class ) // Declare the class of retry here, you can see that if this is the case, every testcase may need to do this, isn't the code a bit too much :( 
    public  void demoTest()
    {
        DemoPage dp=new DemoPage();
        dp.demoTest();
    }
    @Test 
    public  void demoTest2 ()
    {
        DemoPage dp2=new DemoPage();
        dp2.demoTest2();
    }
}
Since it is a framework to write this way, it must be a bit inappropriate.
frame design use                                         
The corresponding interfaces in testNG that provide these functions are as follows:

Interface IRetryAnalyzer    is an interface of retrytestcase, and then implement the corresponding method after implementing this interface:

There is a class RetryAnalyzerCount that already implements the above methods of this interface:

package org.testng.util;

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

import java.util.concurrent.atomic.AtomicInteger;

/**
 * An implementation of IRetryAnalyzer that allows you to specify
 * the maximum number of times you want your test to be retried.
 * 
 * @author [email protected] (Jeremie Lenfant-Engelmann)
 */
public abstract class RetryAnalyzerCount implements IRetryAnalyzer {

  // Default retry once.
  AtomicInteger count = new AtomicInteger(1);

  /**
   * Set the max number of time the method needs to be retried.
   * @param count
   */
  protected void setCount(int count) {
    this.count.set(count);
  }

  /**
   * Retries the test if count is not 0. 
   * @param result The result of the test.
   */
  @Override
  public boolean retry(ITestResult result) {
    boolean retry = false;

    if (count.intValue() > 0) {
      retry = retryMethod(result);
      count.decrementAndGet();
    }
    return retry;
  }

  /**
   * The method implemented by the class that test if the test
   * must be retried or not.
   * @param result The result of the test.
   * @return true if the test must be retried, false otherwise.
   */
  public abstract boolean retryMethod(ITestResult result);
}

 

So as can be seen from the above, if you directly use the inherited RetryAnalyzerCount class, you will save a lot of trouble and you can use it directly.

Class TestListenerAdapter

IConfigurationListenerIConfigurationListener2 , org.testng.internal.IResultListener, org.testng.internal.IResultListener2,  ITestListenerITestNGListener

The above is another class that implements the operation of retry. Not used here.

What we are using today is the IRetryAnalyzer interface, the code is as follows:

    package com.com.baserunner;
    import org.testng.IRetryAnalyzer;
    import org.testng.ITestResult;
    /**
     * @author [email protected]
     * @modify [email protected]
     * @version 1.0
     * @category
     * 
     */

    public class RetryFail  implements IRetryAnalyzer
    {
        private final int m_maxRetries = 1;
        private final int m_sleepBetweenRetries = 1000;
        private int currentTry;
        private String previousTest = null;
        private String currentTest = null;
        public RetryFail()
        {
            currentTry = 0;
        }

        @Override
        public boolean retry(final ITestResult result)
        {
            // If a testcase has succeeded, this function is not called.        
            boolean retValue = false;        
            
            // Getting the max retries from suite.
           // String maxRetriesStr = result.getTestContext().getCurrentXmlTest().getParameter("maxRetries");
           String maxRetriesStr = result.getTestContext().getSuite().getParameter("maxRetries");
            int maxRetries = m_maxRetries;
            if(maxRetriesStr != null)
            {
                try        
                {
                    maxRetries = Integer.parseInt(maxRetriesStr);
                }
                catch (final NumberFormatException e)
                {
                    System.out.println("NumberFormatException while parsing maxRetries from suite file." + e);
                }
            }
           
            // Getting the sleep between retries from suite.you can from the suite parameter 
            String sleepBetweenRetriesStr = result.getTestContext().getSuite().getParameter("sleepBetweenRetries");
            int sleepBetweenRetries = m_sleepBetweenRetries;
            if(sleepBetweenRetriesStr != null)
            {
                try        
                {
                    sleepBetweenRetries = Integer.parseInt(sleepBetweenRetriesStr);
                }
                catch (final NumberFormatException e)
                {
                    System.out.println("NumberFormatException while parsing sleepBetweenRetries from suite file." + e);
                }
            }
            
            currentTest = result.getTestContext().getCurrentXmlTest().getName();
            
            if (previousTest == null)
            {
                previousTest = currentTest;
            }
            if(!(previousTest.equals(currentTest)))
            {
                currentTry = 0;
            }
           
            if (currentTry < maxRetries &&!result.isSuccess())
            {
                try
                {
                    Thread.sleep(sleepBetweenRetries);
                }
                catch (final InterruptedException e)
                {
                    e.printStackTrace ();
                }
                currentTry++;  
                result.setStatus(ITestResult.SUCCESS_PERCENTAGE_FAILURE);
                retValue = true;
                          
            }
            else
            {
                currentTry = 0;
            }
            previousTest = currentTest;
            // if this method returns true, it will rerun the test once again.
            
         
            return retValue;
        }
    }

There is also a lisetner that needs to be added to the testNG configuration file:

package com.coma.baserunner;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import org.testng.IAnnotationTransformer;
import org.testng.IRetryAnalyzer;
import org.testng.annotations.ITestAnnotation;

public class RetryListener implements IAnnotationTransformer {

    @SuppressWarnings("rawtypes")
    @Override
    public void transform(ITestAnnotation annotation, Class testClass,
            Constructor testConstructor, Method testMethod) {

        IRetryAnalyzer retry = annotation.getRetryAnalyzer();
        if (retry == null) {
            //annotation.setRetryAnalyzer(RetryAnalyzer.class);
            annotation.setRetryAnalyzer(RetryFail.class);
        }
    }

}

Then configure the following in the xml configuration file of testNG:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="FirstSuite" parallel="false" >
 <!--  <parameter name="configfile" value="/resources/config.properties"></parameter> -->
 <parameter name="excelpath" value="resources/TestData.xls"></parameter>
 <listeners>
   <listener class-name="com.com.baserunner.RetryListener"></listener>
 </listeners> 

 

 
 

There is no problem with the above configuration method. The only defect is that the number of retry testcases will be counted in the testNG report when running, so it may cause that the number of testcases after running is inaccurate. There are also people on the Internet about this issue. In the discussion, but has not been able to get a good answer.

Recently, I feel that I take a closer look at the source code of testNG to see if I can modify the corresponding testNG report. Make the testcase data displayed in the result consistent with the actual testcase, and the testcase of retry only calculates the last successful run.

If there are results, update again. . . . . . .Smile

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326903778&siteId=291194637