在UI自动化测试用例执行过程中,经常会有很多不确定的因素导致用例执行失败,比如网络原因、环境问题等,所以我们有必要引入重试机制(失败重跑),来提高测试用例执行稳定性。
准备工作:我们在进行失败截图保存到本地的时候,需要用到FileUtils类,该类是在commons-io包下的,所以我们需要先引入依赖:
<!-- https://mvnrepository.com/artifact/commons-io/commons-io --> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>
一:失败用例截图:
1、创建一个用例失败截图监听类(TestResultListener,名字自起)实现IHookable接口,实现run方法。 IHookable接口的作用:动态替换每一个被@Test注解标注的方法,即每当运行到@Test注解的方法的时候,就会执行该类的逻辑。
代码如下:
@Override public void run(IHookCallBack iHookCallBack, ITestResult iTestResult) { //保证@Test注解标注的测试方法能够正常执行 iHookCallBack.runTestMethod(iTestResult); //判断用例结果是否异常 if(iTestResult.getThrowable() != null){ //testResult参数提供了getInstance方法,可以获取当前测试类的实例(对象) BaseTest baseTest = (BaseTest) iTestResult.getInstance(); RemoteWebDriver driver = baseTest.driver; //保存到allure报表中 saveScreenshotToAllure(takeScreenshotAsByte(driver)); //保存到本地 takeScreenshot(driver,"test_"+System.currentTimeMillis()); } }
2、在testng.xml文件中添加listener标签使监听器生效,代码如下:
<!--使监听器生效--> <listeners> <listener class-name="com.lrc.listener.TestResultListener"></listener> </listeners>
3、在Listener类中添加@Attachment注解方法,将截图保存到allure报表中
@Attachment(value = "screenshot",type = "image/png") public byte[] saveScreenshotToAllure(byte[] data){ //返回的字节数组的数据 作为附件添加到Allure报表中--》@Attachment注解来实现的 return data; }
4、在Listener类中提供生成字节数组的截图数据
/** * 生成字节数组的截图数据 * @param driver * @return */ public byte[] takeScreenshotAsByte(RemoteWebDriver driver){ byte[] data = driver.getScreenshotAs(OutputType.BYTES); return data; }
5、在Listener类中提供生成普通文件的截图数据,用于在本地也生成截图
/** * 生成截图以普通文件的形式,并且保存到本地 * @param driver * @param fileName */ public void takeScreenshot(RemoteWebDriver driver, String fileName){ File srcFile = driver.getScreenshotAs(OutputType.FILE); File destFile = new File(System.getProperty("user.dir")+"\\screenshot\\"+fileName+".png"); try { FileUtils.copyFile(srcFile,destFile); } catch (IOException e) { e.printStackTrace(); } }
整个失败用例截图类的代码:
package com.lrc.listener; import com.lrc.common.BaseTest; import io.qameta.allure.Attachment; import org.apache.commons.io.FileUtils; import org.openqa.selenium.OutputType; import org.openqa.selenium.remote.RemoteWebDriver; import org.testng.IHookCallBack; import org.testng.IHookable; import org.testng.ITestResult; import java.io.File; import java.io.IOException; /** * @param * @author lrc * @create 2021/12/19 * @return * @description **/ public class TestResultListener implements IHookable { @Override public void run(IHookCallBack iHookCallBack, ITestResult iTestResult) { //保证@Test注解标注的测试方法能够正常执行 iHookCallBack.runTestMethod(iTestResult); //判断用例结果是否异常 if(iTestResult.getThrowable() != null){ //testResult参数提供了getInstance方法,可以获取当前测试类的实例(对象) BaseTest baseTest = (BaseTest) iTestResult.getInstance(); RemoteWebDriver driver = baseTest.driver; //保存到allure报表中 saveScreenshotToAllure(takeScreenshotAsByte(driver)); //保存到本地 takeScreenshot(driver,"test_"+System.currentTimeMillis()); } } @Attachment(value = "screenshot",type = "image/png") public byte[] saveScreenshotToAllure(byte[] data){ //使用@Attachment注解来实现的返回的字节数组的数据 作为附件添加到Allure报表中 return data; } /** * 生成字节数组的截图数据 * @param driver * @return */ public byte[] takeScreenshotAsByte(RemoteWebDriver driver){ byte[] data = driver.getScreenshotAs(OutputType.BYTES); return data; } /** * 生成截图以普通文件的形式,并且保存到本地 * @param driver * @param fileName */ public void takeScreenshot(RemoteWebDriver driver, String fileName){ File srcFile = driver.getScreenshotAs(OutputType.FILE); File destFile = new File(System.getProperty("user.dir")+"\\screenshot\\"+fileName+".png"); try { FileUtils.copyFile(srcFile,destFile); } catch (IOException e) { e.printStackTrace(); } } }
整个testng.xml的代码:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="All Test Suite" parallel="tests" thread-count="2"> <!--使监听器生效--> <listeners> <listener class-name="com.lrc.listener.TestResultListener"></listener> </listeners> <test name="测试"> <classes> <class name="com.lrc.testcases.TestBaidu3"/> </classes> </test> </suite>
下面,我们特意设置用例执行失败,查看用例失败截图是否会生成在allure报表中生成与在本地生成
执行结果:
(1)在本地文件有生成失败用例截图
在allure报表里也有生成失败用例截图:
二:失败用例重试
1、创建用例重试监听类(RetryListener,名字自起)实现testng包下的IRetryAnalyzer类,重写retry方法。
package com.lrc.listener; import org.testng.IRetryAnalyzer; import org.testng.ITestResult; /** * @param * @author lrc * @create 2021/12/20 * @return * @description **/ public class RetryListener implements IRetryAnalyzer { //最大重试次数 private int maxRetryCount=3; //当前的重试次数 private int currentRetryCount=0; @Override public boolean retry(ITestResult result) { //限制重试的最大次数,否则会进入死循环 if(currentRetryCount < maxRetryCount) { //如果当前的重试次数没有达到限制,就去执行重试机制 currentRetryCount++; return true; }else { return false; } } }
2、添加一个全局注解属性修改的类,用于使@Test注解每次都能拥有retryAnalyzer属性,可以减去每个@Test注解都要配置retryAnalyzer属性操作
package com.lrc.listener; import org.testng.IAnnotationTransformer; import org.testng.IRetryAnalyzer; import org.testng.annotations.ITestAnnotation; import java.lang.reflect.Constructor; import java.lang.reflect.Method; /** * @param * @author lrc * @create 2021/12/20 * @return * @description **/ public class GlobalAnnotationTransformer implements IAnnotationTransformer { //通过实现IAnnotationTransformer接口可以动态的修改@Test注解的属性 public void transform(ITestAnnotation annotation, Class testClass, Constructor testConstructor, Method testMethod) { // 获取@Test注解的RetryAnalyzer属性对象 IRetryAnalyzer iRetryAnalyzer = annotation.getRetryAnalyzer(); if (iRetryAnalyzer == null) { annotation.setRetryAnalyzer(RetryListener.class); } } }
3、在testng.xml中添加listener标签,使得全局注解修改监听类生效
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE suite SYSTEM "http://testng.org/testng-1.0.dtd"> <suite name="All Test Suite" parallel="tests" thread-count="2"> <!--使监听器生效--> <listeners> <listener class-name="com.lrc.listener.TestResultListener"></listener> <listener class-name="com.lrc.listener.GlobalAnnotationTransformer"></listener> </listeners> <test name="测试"> <classes> <class name="com.lrc.testcases.TestBaidu3"/> </classes> </test> </suite>
下面,我们再来看看失败用例是否会重新运行,最大运行4次,由于上面我特意断言每个用例都失败,所以每个用例都应该运行4次:
每次用户执行失败,都会在本地生成失败截图,如下:
allure报表也会有失败截图,并且监听了当前用例失败重跑了几次:
在allure报表中看到Retries被重复执行了3次,点击每一次的执行结果,都会展示错误截图:
至此,失败用例截图与失败用例重试已经集成完成。