测试用例运行稳定性是自动化质量的一个重要指标,在运行中需要尽可能的剔除非bug造成的测试用例执行失败,对于失败用例进行重跑是常用策略之一。一种重跑策略是所有用例运行结束后对失败用例重跑,另一种重跑策略是在运行时监控用例运行状态,失败后实时重跑。
下面,详细介绍TestNG如何对失败测试用例实时重跑并解决重跑过程中所遇到问题的实践和解决方案。对失败测试用例进行实时重跑,有以下几个方面需求:
测试用例运行失败,监听到失败后立即进行重跑
测试用例通过dependsOnMethods/dependsOnGroups标记依赖其他测试用例,在被依赖的测试用例重跑运行成功后,该测试用例可以继续运行
对于重跑多次的测试用例,只记录最后一次运行成功或失败结果
第一部分 测试用例重跑 1.1 retryAnalyzer注解方式对于希望测试用例中的少量易失败,不稳定的测试用例进行重跑,可采用这种方式。
1.1.1 原理以下是TestNG处理测试用例运行结果的部分代码。
IRetryAnalyzer retryAnalyzer = testMethod.getRetryAnalyzer(); boolean willRetry = retryAnalyzer != null && status == ITestResult.FAILURE && failure.instances != null && retryAnalyzer.retry(testResult); if (willRetry) { resultsToRetry.add(testResult); failure.count++; failure.instances.add(testResult.getInstance()); testResult.setStatus(ITestResult.SKIP); } else { testResult.setStatus(status); if (status == ITestResult.FAILURE && !handled) { handleException(ite, testMethod, testResult, failure.count++); }分析以上代码,其中,接口IretryAnalyzer的方法retry()的返回值作为是否对失败测试用例进行重跑的一个条件。如果retry()结果为true,则该失败测试用例会重跑,同时将本次失败结果修改为Skip;如果结果为false,则失败的测试用例保持失败结果,运行结束。因此,如果你希望失败测试用例重跑的话,需要把IretryAnalyzer的retry()方法重写,插入自己定义的逻辑,设置返回值为true。
1.1.2 代码创建类RetryImpl,重写retry()方法,设置失败测试用例的重跑次数,代码如下,:
public class RetryImpl implements IRetryAnalyzer { private int count = 1; private int max_count = 3; // Failed test cases could be run 3 times at most @Override public boolean retry(ITestResult result) { System.out.println("Test case :"+result.getName()+",retry time: "+count+""); if (count < max_count) { count++; return true; } return false; } } 1.1.3 实例 public class TestNGReRunDemo { @Test(retryAnalyzer=RetryImpl.class) public void test01(){ Assert.assertEquals("success","fail"); System.out.println("test01"); } }以上测试用例test01可重复运行3次。
1.2 实现接口IAnnotationTransformer方法如果希望所有失败的测试用例都进行重跑,采用retryAnalyzer注解方式对每个测试用例进行注解就比较麻烦。通过实现IAnnotationTransformer接口的方式,可以对全量测试用例的重试类进行设置。
该接口是一个监听器接口,用来修改TestNG注解。IAnnotationTransformer监听器接口只有一个方法:transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod). 上文中,我们自定义了类RetryImpl 实现接口IRetryAnalyzer。TestNG通过transfrom()方法修改retryAnalyzer注解。以下代码对retryAnalyzer注解进行修改设置。
创建类RetryListener,代码如下。
public class RetryListener implements IAnnotationTransformer { public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) { IRetryAnalyzer retry = annotation.getRetryAnalyzer(); if (retry == null) { annotation.setRetryAnalyzer(RetryImpl.class); } } } 1.2.2 配置ListenerTestNG可以在配置文件或者测试类中对Listener类进行配置。
方法一:在TestNG的配置XML中进行以下配置
<listeners> <listener class-name="PackageName.RetryListener"></listener> </listeners>方法二:在测试类中通过@Listeners配置
@Listeners({RetryListener.class}) public class TestNGReRunDemo { @Test public void test01(){ Assert.assertEquals("success","fail"); System.out.println("test01"); } }配置完成后,运行测试用例test01,运行结果显示test01将重跑次数3次。
第二部分 被依赖的测试用例重跑结果处理进一步分析TestNG的运行代码,其在对失败运行用例重跑时,逻辑如下图。
对于通过dependsOnMethods 或dependsOnGroups注解依赖于其他测试用例的测试用例来讲,测试用例执行分为两种情况:
alwaysRun=true,则无论所依赖的测试用例执行情况如何,该测试用例都会执行,即所依赖的测试用例重跑不会影响该测试用例的执行。