写在前面
在执行自动化的时候,往往有很多时候用例执行失败不是代码或者用例本身的问题,例如app自动化的时候网络波动,手机卡顿等问题也会导致执行失败。这就要引进testNG中的失败用例重跑机制,可以让自动化执行最大避免这些外部因素的影响(当然如果重跑的时候还是手机卡顿,信号不好的话,恩~~,你自己品一下吧~~)失败用例重跑我们可以根据需要设定为全局或者局部,局部的话只需要在要执行的用例的@Test注解属性里面添加对应的retryAnalyzer属性即可,这里我们关注的是全局重跑。
操作步骤
1.实现(implements)IRetryAnalyzer接口,重写(override)retry方法
2.实现(implements)IAnnotationTransformer接口,重写(override)transform方法
3.在testng.xml文件中设置监听
实现步骤
新建一个类TestCaseRetry,implements IRetryAnalyzer接口。该类的作用是设定失败的用例重跑需要重新运行的次数,设定一个运行次数,设定一个最大执行次数,当运行次数超过最大执行次数的时候,说明重跑完成。
retry方法返回值如果为true说明重跑未完成,返回值为false,说明重跑完成
public class TestCaseRetry implements IRetryAnalyzer {
private Logger logger = Logger.getLogger(TestCaseRetry.class);
//当前正在进行的重试次数
public static int currentCount = 1;
//允许重试的最大次数
private int maxCount = 2;
@Override
//如果返回为true的话,则执行retry
public boolean retry(ITestResult iTestResult) {
logger.info("当前重试次数是【" + currentCount + "】");
if (currentCount <= maxCount) {
logger.info("当前测试方法【" + iTestResult.getName() + "】执行失败,进入失败用例重试模式,正在进行第【" + currentCount + "】次重试");
currentCount++;
return true;
}
return false;
}
}
新建一个类RetryListener,implements IAnnotationTransformer接口。该类的作用是用来监听所有的测试方法是否有retryAnalyzer注解属性,如果有该属性则不会执行我们设定的重跑机制(局部重跑的优先级比全局的高)。
public class RetryListener implements IAnnotationTransformer {
@Override
public void transform(ITestAnnotation iTestAnnotation, Class aClass, Constructor constructor, Method method) {
//1.初始化IRetryAnalyzer对象
IRetryAnalyzer iRetryAnalyzer = iTestAnnotation.getRetryAnalyzer();
//判断测试方法是否设置重跑属性
if (iRetryAnalyzer == null) {
//为空说明用例方法里面未设置重跑,则按照全局设置进行用例重跑
//如果用例方法设置了注解属性retryAnalyzer,则按照注解的重跑
iTestAnnotation.setRetryAnalyzer(TestCaseRetry.class);
}
}
}
将监听类RetryListener添加到testng.xml文件中
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd">
<suite name="app_auto">
<test name="login">
<classes>
<class name="cases.LoginTest"></class>
</classes>
</test>
<!--配置监听器-->
<listeners>
<!--监听用例执行情况-->
<listener class-name="listener.CustomListener"></listener>
<!--对用例方法执行进行监听是否需要重试-->
<listener class-name="listener.RetryListener"></listener>
</listeners>
</suite>
那就是在第一步设定重跑次数的时候,没有设定什么时候把正在重跑的次数(currentCount)还原为初始值,这样就会导致只有在执行第一个失败用例的时候会进行重跑,遇到第二个失败用例,因为当前的运行次数已经超过最大次数了,但是并没有进行还原,所以后面的失败用例永远不会进入重跑。
那怎么去解决这个问题呢?
失败的用例在执行重跑的时候,要么重跑成功,要么依然失败(当前不考虑skip,但是也可以加上),那我们监听用例的执行结果,在用例执行有结果以后,我们重置执行次数就可以了。
新建一个CustomListener类,implements TestListenerAdapter接口,重写其中的onTestFailure和onTestSuccess方法。这两个方法为用例执行以后失败和成功分别调用的方法,比如用例执行失败需要截图,就可以在onTestFailure方法里面去实现,这样用例执行失败以后,会进入该方法完成截图操作。而我们只需要在这两个方法里面将对面的运行重跑次数恢复初始值即可。这样不管重跑几次的失败用例执行结果是成功还是失败都必然会让重跑次数恢复默认值,其他失败用例再次执行重跑的时候就可以不受影响。
public class CustomListener extends TestListenerAdapter {
Logger logger = Logger.getLogger(CustomListener.class);
@Override
public void onTestFailure(ITestResult tr) {
super.onTestFailure(tr);//如果测试用例执行完所有重试以后依然失败的话,重新设置currentCount为初始值,方便其他用例执行重试
logger.info("用例执行失败,重试机制的次数恢复初始值");
TestCaseRetry.currentCount = 1;
}
@Override
public void onTestSuccess(ITestResult tr) {
//如果测试用例执行重试的时候,执行成功了,也需要设置currentCount为初始值,方便其他用例执行重试
logger.info("用例执行成功,重试机制的次数恢复初始值");
TestCaseRetry.currentCount = 1;
super.onTestSuccess(tr);
}
这样,当用例执行的时候,根据设定的重跑次数(当前设定为2),每条用例执行以后,如果失败会再重跑2次,所以每条用例一共会执行3次。通过如下日志可以看到,当用例第一次手机号和密码都输入空的时候登录,用例执行失败,进入用例重跑,执行2次以后,当重跑次数为3的时候,自动进入失败用例截图然后重置重跑次数,进入下一条用例数据(手机号为123,密码为空)。
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【手机号】输入【】
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【密码】输入【】
method: Base.Base.click(Base.java:260)----在【登录页面】里面点击【登录】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:26)----当前重试次数是【1】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:28)----当前测试方法【testFailureLogin】执行失败,进入失败用例重试模式,正在进行第【1】次重试
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【手机号】输入【】
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【密码】输入【】
method: Base.Base.click(Base.java:260)----在【登录页面】里面点击【登录】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:26)----当前重试次数是【2】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:28)----当前测试方法【testFailureLogin】执行失败,进入失败用例重试模式,正在进行第【2】次重试
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【手机号】输入【】
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【密码】输入【】
method: Base.Base.click(Base.java:260)----在【登录页面】里面点击【登录】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:26)----当前重试次数是【3】
method: util.ScreenshotUtil.saveScreenshot(ScreenshotUtil.java:32)----拷贝文件截图到指定目录【test-output\screenshot\login\2020-03-28\1585325814058.jpg】
method: listener.CustomListener.onTestFailure(CustomListener.java:40)----用例执行失败,重试机制的次数恢复初始值
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【手机号】输入【123】
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【密码】输入【】
method: Base.Base.click(Base.java:260)----在【登录页面】里面点击【登录】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:26)----当前重试次数是【1】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:28)----当前测试方法【testFailureLogin】执行失败,进入失败用例重试模式,正在进行第【1】次重试
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【手机号】输入【123】
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【密码】输入【】
method: Base.Base.click(Base.java:260)----在【登录页面】里面点击【登录】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:26)----当前重试次数是【2】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:28)----当前测试方法【testFailureLogin】执行失败,进入失败用例重试模式,正在进行第【2】次重试
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【手机号】输入【123】
method: Base.Base.input(Base.java:272)----给【登录页面】里面的【密码】输入【】
method: Base.Base.click(Base.java:260)----在【登录页面】里面点击【登录】
method: listener.TestCaseRetry.retry(TestCaseRetry.java:26)----当前重试次数是【3】
method: util.ScreenshotUtil.saveScreenshot(ScreenshotUtil.java:32)----拷贝文件截图到指定目录【test-output\screenshot\login\2020-03-28\1585325821071.jpg】
method: listener.CustomListener.onTestFailure(CustomListener.java:40)----用例执行失败,重试机制的次数恢复初始值
但是!但是!这里会有另一个testNG的版本问题,之前用的版本为6.9.10的时候,同样的用例,第一条数据执行了3次,第二条数据执行了6次,第三条数据执行了9次,越往后面的数据执行,运行次数就越多。这是testNG版本问题,更换为高版本以后搞定!当前使用6.14.3正常!
左图:6.9.10版本:手机号123的用例执行了6次 右图:6.14.3版本:手机号123的用例执行了3次