天天看点

实现testNg的retry机制

Testng 的重试机制 在不写代码的情况没有提供可配置方式,需要自己实现其提供的接口,并以监听器的方式提供出来才可使用,具体步骤如下:

1.首先需要实现 IRetryAnalyzer接口

package ec.qa.autotest.ui.testng.listener;

import org.testng.IRetryAnalyzer;
import org.testng.ITestResult;
import org.testng.Reporter;
import ec.qa.autotest.ui.constants.CommonConstants;
import ec.qa.autotest.ui.testbase.TestBase;
/**
 * @author xin.wang
 *实现testng接口实现用例失败重跑
 */
public class RetryToRunCase implements IRetryAnalyzer{

	private int retryCount = 1;

	private static int maxRetryCount;
	
	public int getRetryCount() {
		return retryCount;
	}
		
	public static int getMaxRetryCount() {
		return maxRetryCount;
	}
	
	@SuppressWarnings("static-access")
	public RetryToRunCase(){
		this.maxRetryCount = CommonConstants.RETRY_COUNT;
	}
	
	public boolean retry(ITestResult result) {
		if (retryCount <= maxRetryCount) {
			Reporter.setCurrentTestResult(result);
			TestBase.success = false;
			retryCount++;
			return true;
		}
		return false;
	}
}
           

2.需要实现testNg监听器的2个接口,onFinish()方法的代码是为了在测试结果报告中剔除掉失败后重试成功的结果.

package ec.qa.autotest.ui.testng.listener;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import org.testng.IAnnotationTransformer;
import org.testng.IRetryAnalyzer;
import org.testng.ITestContext;
import org.testng.ITestListener;
import org.testng.ITestResult;
import org.testng.annotations.ITestAnnotation;

/**
 * @author xin.wang
 * 用例失败重跑的监听器
 */
public class TestngRetryListener implements IAnnotationTransformer,ITestListener {
	@SuppressWarnings("rawtypes")
	public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) {
		IRetryAnalyzer retry = annotation.getRetryAnalyzer();
		if (retry == null) {
			annotation.setRetryAnalyzer(RetryToRunCase.class);
		}
	}
	
	public void onFinish(ITestContext testContext) {
		ArrayList<ITestResult> testsToBeRemoved = new ArrayList<ITestResult>();
		Set<Integer> passedTestIds = new HashSet<Integer>();
		for (ITestResult passedTest : testContext.getPassedTests().getAllResults()) {
			passedTestIds.add(getId(passedTest));
		}

		Set<Integer> failedTestIds = new HashSet<Integer>();
		for (ITestResult failedTest : testContext.getFailedTests().getAllResults()) {
			int failedTestId = getId(failedTest);
			if (failedTestIds.contains(failedTestId) || passedTestIds.contains(failedTestId)) {
				testsToBeRemoved.add(failedTest);
			} else {
				failedTestIds.add(failedTestId);
			}
		}

		for (Iterator<ITestResult> iterator = testContext.getFailedTests().getAllResults().iterator(); iterator
				.hasNext();) {
			ITestResult testResult = iterator.next();
			if (testsToBeRemoved.contains(testResult)) {
				iterator.remove();
			}
		}

	}

	private int getId(ITestResult result) {
		int id = result.getTestClass().getName().hashCode();
		id = id + result.getMethod().getMethodName().hashCode();
		id = id + (result.getParameters() != null ? Arrays.hashCode(result.getParameters()) : 0);
		return id;
	}
	
	public void onTestFailure(ITestResult result) {
	}
	public void onTestStart(ITestResult result) {
		// TODO Auto-generated method stub
		
	}
	public void onTestSuccess(ITestResult result) {
		// TODO Auto-generated method stub
	}
	public void onTestSkipped(ITestResult result) {
		// TODO Auto-generated method stub
		
	}
	public void onTestFailedButWithinSuccessPercentage(ITestResult result) {
		// TODO Auto-generated method stub
		
	}
	public void onStart(ITestContext context) {
		// TODO Auto-generated method stub
		
	}
}
           

添加testng监听器的方法很多如下 个人习惯 命令行方式和基于testng的XML文件配置方式:

监听器的使用方法

前文已讲过,监听器的编码过程就是定义一个 Java 类实现监听器接口。下面简单介绍一下监听器的几种使用方法。

在 testng.xml 中使用 TestNG 监听器

TestNG 通过 testng.xml 配置所有的测试方法。Testng.xml 提供了 listeners 和 listener 标签用来添加自定义的监听器。下面示范的是本文示例代码中包含的 testng.xml 文件。

<suite name="TestNGSample">
	<listeners>
		<listener class-name="listeners.OSFilter" />
		<listener class-name="listeners.ProgressTracker" />
	</listeners>
	<test name="ProgressTracker Demo">
		<classes>
			<class name="tests.SampleTest" />
		</classes>
	</test>
</suite>      

在源代码中使用 TestNG 监听器

通过 @Listeners 注释,可以直接在 Java 源代码中添加 TestNG 监听器。下面示范的是本文示例代码中如何使用 @Listeners 注释。

@Listeners({ OSFilter.class, ProgressTracker.class })
public class SampleTest {

	@Test(groups = { OSNames.OS_LINUX })
	public void test1() {
		sleep(5000);
		System.out.println(">>>test1");
	}      

值得注意的是:

  • 在 @Listeners 中添加监听器跟在 testng.xml 添加监听器一样,将被应用到整个测试套件中的测试方法。如果需要控制监听器的应用范围(比如添加的监听器仅使用于某些测试测试类或者某些测试方法),则必须在监听器类中编写适当的判断逻辑。
  • 在 @Listeners 中添加监听器跟在 testng.xml 添加监听器的不同之处在于,它不能添加 IAnnotationTransformer 和 IAnnotationTransformer2 监听器。原因是因为这两种监听器必须在更早的阶段添加到 TestNG 中才能实施修改注释的操作,所以它们只能在 testng.xml 添加。
  • TestNG 对添加的监听器不做去重判断。因此,如果 testng.xml 和源代码中添加了相同的监听器,该监听器的方法会被调用两次。有关这一点,大家可以通过运行本文附带的示例代码包中 testng.xml 验证。因此,切记,不要通过多种方式重复添加监听器。

通过 ServiceLoader 使用 TestNG 监听器

Java SE 6 开始提供了 ServiceLoader。它可以帮助用户查找、加载和使用服务提供程序,从而在无需修改原有代码的情况下轻易地扩展目标应用程序。通过 ServiceLoader 的方式使用 TestNG 监听器,简单来说,就是创建一个 jar 文件,里面包含 TestNG 监听器的实现类已经 ServiceLoader 需要的配置信息,并在运行 TestNG 时把该 jar 文件加载到类路径中。具体步骤请查阅 TestNG 官方文档。这样做的好处是:

  1. 可以轻松地与其他人分享 TestNG 监听器。
  2. 当有很多 testng.xml 文件时,不需要重复把监听器添加到每个文件中。

通过命令行使用 TestNG 监听器

通过命令行使用 TestNG 监听器,需要在命令行中加入”-listener”参数。如要指定多个监听器,用逗号分隔。下面是一个调用的示例。

java org.testng.TestNG -listener MyListener testng1.xml [testng2.xml testng3.xml ...]      

通过 IDE 使用 TestNG 监听器

TestNG 在多种 IDE 中都有插件支持,比如 Eclipse 和 IDEA。因为最终 IDE 也是以命令行的方式调用 TestNG,因此在 IDE 中也是通过添加“-listener”参数使用 TestNG 监听器。下图以 Eclipse 为例示范了 TestNG 监听器的配置方法。

图 1. Eclipse 中 TestNG 监听器的配置
实现testNg的retry机制

继续阅读