“哎吆,怎么这么多执行失败呢?”,当你看到自动化测试报告中,尤其是ui自动化测试报告中,大面积执行失败,并且最后显示的原因均是页面元素timeout exception的时候,是不是觉得一个脑袋两个大,虽然我们已经加了等待时间,无论是隐式等待还是显示等待,也不能完全解决该类问题!那有没有啥好的方法来解决这个问题,以便提高自动化测试的稳定性呢?
哈哈,答案是肯定的,那就是我们今天的主题,自动重试实现方法--AOP模式。相信有些老铁会问了:什么是aop,我们当如何去自动重试呢?AOP为Aspect Oriented Programming的缩写,是面向切面编程,通过预编译方式和运行其动态代理实现程序功能的统一维护的一种技术。今天我们以百度搜索首页为例,来详细给大家分享-aop模式的应用。
接着简单给大家介绍一下自动化case的场景:进入百度首页,在搜索框内输入要搜索的关键字,并点击搜索按钮。这里包含了百度首页页面的两个元素,搜索输入框和搜索按钮。假设搜索输入框元素一直加载不出来,我们要对整个输入关键字的动作进行重试。而整个输入关键字的动作是一个方法,如下:
public void sendKeysForSearchInput(String content){
searchInput.waitForPresent();
searchInput.clear();
searchInput.sendKeys(content);
}
那么如何来通过aop来实现我们的重试目标呢?步骤如下:
首先,要创建一个模板RetryTemplate类文件,将要重试的次数、重试的间隔时间和什么条件下重试定义于该模板中,以便后面的aop切面类中调用,具体的实现代码如下。
public abstract class RetryTemplate {
private static final int DEFAULT_RETRY_COUNT =0;
private int retryCount =DEFAULT_RETRY_COUNT;
// reset sleep time
private int sleepTime =0;
public int getSleepTime() {
return sleepTime;
}
public RetryTemplate setSleepTime(int sleepTime) {
if(sleepTime <0) {
throw new IllegalArgumentException("sleepTime shall be equal or bigger than 0");
}
this.sleepTime = sleepTime;
return this;
}
public int getRetryCount() {
return retryCount;
}
public RetryTemplate setRetryCount(int retryCount) {
if (retryCount <=0) {
throw new IllegalArgumentException("retryCount shall bigger than 0");
}
this.retryCount = retryCount;
return this;
}
/**
* retry for business,throw exception when fail;
* whether retry or not by returning status;
*
@return
*/
protected abstract Object toDo()throws Throwable;
public Object execute()throws InterruptedException {
for (int i =0; i <=retryCount; i++) {
try {
return toDo();
}catch (Throwable e) {
System.out.println("run business: "+ (i+1) +" times");
System.out.println("run business exception,e: "+e);
Thread.sleep(sleepTime);
}
}
return null;
}
}
其次,创建一个注解RetryFunction, 以便我们在需要重试的method中使用。
具体代码如下,其中定义了count和sleep参数,count传入method需要重试的次数,sleep传入两次重试的间隔时间。
import java.lang.annotation.*;
@Documented
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RetryFunction {
/**
* retryCount
* @return
*/
int count()default 0;
/**
* retry interval Time
* @return
*/
int sleep()default 0;
}
然后,也是我们最核心的一步,定义我们的aop,即新建RetryAspect类文件。实现代码如下:
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
@Slf4j
public class RetryAspect {
@Around(value ="@annotation(retryFunction)")
public Object execute(ProceedingJoinPoint joinPoint,RetryFunction retryFunction)throws Exception {
RetryTemplate retryTemplate =new RetryTemplate() {
@Override
protected Object toDo()throws Throwable {
return joinPoint.proceed();
}
};
retryTemplate.setRetryCount(retryFunction.count()).setSleepTime(retryFunction.sleep());
return retryTemplate.execute();
}
}
是不是这样就算结束了,可以正常使用了呢,那肯定还是不行的,我们还需要配置aop.xml文件(base-package要配置正确,否则不能生效):
<?xml version="1.0" endcoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="src.test.java"/>
<context:annotation-config/>
<aop:aspectj-autoproxy/>
</beans>
前面三步完成后,最后我们需要引用一下RetryFuction注解,看看实际的效果呢!回到文章开头,介绍百度搜索首页的输入方法sendKeysForSearchInput。只需要在该方法上加上注解RetryFunction并配置想要重试的次数和时间即可,如下:
@RetryFunction(count =1,sleep =2)
public void sendKeysForSearchInput(String content){
searchInput.waitForPresent();
searchInput.clear();
searchInput.sendKeys(content);
}
运行的结果实际结果,如下图所示,成功实现了sendKeysForSearchInput的重试。
最后,我们用实践见识了aop的魅力,为实现方法级别的重试,确实是不错的解决方法。避免了强入侵业务代码,方便方法自动重试。