框架基于 PO 模型进行设计,将页面元素与操作进行拆分,减少页面改动时的维护成本;同时使用 xsd 自定义 xml 标签,通过解析 xml 来驱动 selenium 进行执行,减少了一定的语言学习成本。
主要功能
设计初期的明显问题
Q1:如果只能调用内置方法的话,灵活性不够
A:因此引入了 Spring 作为实例管理,这样就只需要在流程数据中使用指定标签 +beanName 就可以实现自定义方法的执行了
Q2:webUI 自动化通常执行时间过长,如何缩短?
A:引入 selenium-grid 做分布式执行,并且可以隔离数据
Q3:元素定位有没有更为准确和快捷的方式?
A:引入特征识别、模板匹配、OCR 等图像算法
使用具备自描述性的 XML 进行脚本编写,降低门槛
如果想要新增标签/属性,在 XSD 中定义和对应的 entity 增加字段后,在 AutoTestSuiteParser 类中增加赋值即可
通过 ref 标签引用 element 文件
即可使用,重复引用可实现复用
example:
通过指定 html 标签的属性获取对应值,和 variableName 以 kv 的形式存储在 threadLocal 的 Map 中,从而在同一 testFlow 的后续流程中以 $variableName 进行提取,与期望值比较
使用 JavaCV 进行模板匹配并点击的操作
eg:
使用属性 templateImgName 即可
模板图片存放于 src/test/template_img 下,源图片则是当前浏览器窗口截图
tips:使用模板匹配时,不建议在本地执行,会大概率失败,因为本地操作可能会干扰鼠标原始定位
通过反射读取 engine.properties 文件中的配置,可动态配置并发执行用例数
并发执行时将会 send task 到 selenium-hub,并进行分发,加快执行速度
使用 custom 标签时,指定 customFunction 指定类的的 bean 名称,并且该类应该实现 ICustomAction 的 execute 方法
方法 bean 名称为@Service() 中的 beanName
version: "3"
services:
selenium-hub:
image: seleniarm/hub:4.0.0-beta-1-20210215
container_name: selenium-hub-arm
ports:
- "4444:4444"
environment:
- GRID_MAX_SESSION=50
- GRID_TIMEOUT=200
- START_XVFB=false
- GRID_CLEAN_UP_CYCLE=200
chrome:
image: seleniarm/node-chromium:4.0.0-beta-1-20210215
volumes:
- /dev/shm:/dev/shm
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_PUBLISH_PORT=4444
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
- NODE_MAX_INSTANCES=5
- NODE_MAX_SESSION=5
- GRID_CLEAN_UP_CYCLE=200
version: "3"
services:
selenium-hub:
image: selenium/hub
container_name: selenium-hub
ports:
- "4444:4444"
environment:
- GRID_MAX_SESSION=50
- GRID_TIMEOUT=900
- START_XVFB=false
chrome:
image: selenium/node-chrome
volumes:
- /dev/shm:/dev/shm
depends_on:
- selenium-hub
environment:
- SE_EVENT_BUS_PUBLISH_PORT=4444
- SE_EVENT_BUS_HOST=selenium-hub
- SE_EVENT_BUS_SUBSCRIBE_PORT=4443
- NODE_MAX_INSTANCES=5
- NODE_MAX_SESSION=5
- GRID_CLEAN_UP_CYCLE=200
#! /bin/sh
docker-compose up -d --scale chrome=2
chrome=2 表示 selenium-grid 集群的节点数量,chrome 为 docker-compose 中的 service name
启动 shell 脚本,docker ps 查看是否启动成功
访问 localhost:4444 / 虚拟机 ip:4444
创建 Dockerfile 文件
FROM node:alpine
LABEL maintainer="lain"
RUN mkdir -p ./app
WORKDIR ./app
RUN apk add --no-cache git
RUN git clone https://gist.github.com/3c4f35a41c58a55a0ffd00c3e64142c8.git tmpChange
RUN git clone https://github.com/anshooarora/extentx.git
RUN mv tmpChange/connections.js extentx/config/connections.js
RUN rm -rf tmpChange
WORKDIR ./extentx
EXPOSE 1337
RUN npm set registry https://registry.npm.taobao.org
RUN npm config set registry https://registry.npm.taobao.org
RUN npm config set disturl https://npm.taobao.org/dist
RUN npm install
CMD ["./node_modules/.bin/sails", "lift"]
构建镜像docker build -t extentx .
创建 docker-compose.yml 文件
version: '3.6'
services:
extentx:
build: .
environment:
- MONGODB_PORT_27017_TCP_ADDR=mongo
links:
- mongo:mongo
ports:
- 1337:1337
mongo:
image: mongo:3.4
ports:
- 27010:27017
docker-compose up -d
启动
访问 localhost:1337 即可
下载附在项目中的 extentsReport-1.0-SNAPSHOT.jar ,提取出 jar 包中的 pom 文件,和 jar 放在同一级目录下,执行命令mvn install:install-file -Dfile=extentsReport-1.0-SNAPSHOT.jar -DartifactId=extentsReport -DgroupId=com.antigenmhc -Dversion=1.0-SNAPSHOT -Dpackaging=jar -DpomFile=pom.xml
即可
前置环境为已下载 arm64 版本的 JDK
# 升级 brew
brew update
# 安装 cmake
brew install cmake
# 安装 ant
brew install ant
export JAVA_HOME=/Library/Java/JavaVirtualMachines/jdk1.8.0_331.jdk/Contents/Home
export PATH=$JAVA_HOME/bin:$PATH
export JAVA_AWT_INCLUDE_PATH=$JAVA_HOME
export JAVA_AWT_LIBRARY=$JAVA_HOME
export JAVA_INCLUDE_PATH=$JAVA_HOME/indclude
export JAVA_INCLUDE_PATH2=$JAVA_HOME/include/darwin
export JAVA_JVM_LIBRARY=$JAVA_HOME
brew edit opencv
找到-DBUILD_opencv_java=OFF
修改为-DBUILD_opencv_java=ON
在上面的参数之后添加参数:-DOPENCV_JAVA_TARGET_VERSION=1.8
,防止使用高版本 JDK 进行编译
brew install --build-from-source opencv
生成 jar 和 dylib:成功后在 /opt/homebrew/Cellar/opencv/4.7.0/share/java/opencv4 这里会生成 libopencv_java470.dylib 和 opencv-470.jar
将 jar 和 dylib 加入项目
设置项目 JDK 为刚才用来编辑 (即 JAVA_HOME ) 的 JDK
引入依赖
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>1.5.7</version>
</dependency>
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacpp</artifactId>
<version>1.5.7</version>
</dependency>
//待匹配图片
Mat src = imread("filePath",Imgcodecs.IMREAD_GRAYSCALE);
Mat mInput=src.clone();
// 获取匹配模板
Mat mTemplate = imread("filePath",Imgcodecs.IMREAD_GRAYSCALE);
/**
* TM_SQDIFF = 0, 平方差匹配法,最好的匹配为0,值越大匹配越差
* TM_SQDIFF_NORMED = 1,归一化平方差匹配法
* TM_CCORR = 2,相关匹配法,采用乘法操作,数值越大表明匹配越好
* TM_CCORR_NORMED = 3,归一化相关匹配法
* TM_CCOEFF = 4,相关系数匹配法,最好的匹配为1,-1表示最差的匹配
* TM_CCOEFF_NORMED = 5;归一化相关系数匹配法
*/
int resultRows = mInput.rows() - mTemplate.rows() + 1;
int resultCols = mInput.cols() - mTemplate.cols() + 1;
Mat gResult = new Mat(resultRows, resultCols, CvType.CV_32FC1);
Imgproc.matchTemplate(mInput, mTemplate, gResult, Imgproc.TM_CCORR_NORMED);
Core.normalize(gResult, gResult, 0, 1, Core.NORM_MINMAX, -1, new Mat());
Core.MinMaxLocResult mmlr = Core.minMaxLoc(gResult);
Point matchLocation = mmlr.maxLoc;
double x = matchLocation.x + (mTemplate.cols() / 2);
double y = matchLocation.y + (mTemplate.rows() / 2);
System.out.println(new Point(x, y));
项目的大致结构如下
qa-ui-test-demo
├─ screen:截图
├─ src
│ ├─ main
│ │ └─ resources
│ │ ├─ css
│ │ ├─ driver:浏览器驱动
│ │ │ ├─ chromedriver
│ │ │ └─ geckodriver
│ │ ├─ cc.properties:邮件发送方邮箱
│ │ ├─ extentx.properties:连接 extentx 平台配置
│ │ ├─ engine.properties:测试报告以及核心配置
│ │ ├─ mail.properties:邮件配置
│ │ └─ sendto.properties:邮件接收方邮箱
│ └─ test
│ ├─ java
│ │ └─ com
│ │ └─ qalain
│ │ └─ ui
│ │ └─ action:java 脚本
│ └─ resources
│ ├─ page:page xml,用来转为 page pojo 的
│ │ └─ demo-baidu.xml
│ ├─ suiteflow:自动化测试流程文件
│ │ └─ demo-flow.xml
│ └─ suite-testng.xml
├─ pom.xml
└─ README.md
因为经过 xml 二次封装了,所以需要了解常见标签
在执行完测试后,项目的 test-output 文件夹下会出现测试报告
同时也会把测试结果统计到 extentx 上
框架的设计借鉴了许多其它自动化框架,仍然有许多可以完善的地方。由于我是去年校招刚入行半年的新手,所以可能有很多痛点感受不到,导致现在做的功能可能有用也可能是鸡肋。希望大佬们能够多多提一些意见,感谢~