appium测试中,经常遇见下拉选择控件,对于ListView类型的可以直接调用AndroidUIAutomator方法来进行定位
- 具体方法可参考另一篇博客:appium测试-如何滑屏定位指定元素(Android)以及定位失败解决方法
但是有时候会遇见非原生的View控件,如下情况:
- 无法定位获取控件内部选项的任何信息,也就无法用常规的id或xpath的方式去进行定位操作了
第一种比较"笨"的方法就是记住需要定位元素的固定位置或坐标,然后模拟手指操作滑动固定的次数或距离,直接选中确定,这种方法不灵活,显得不智能~
这里分享一下我的解决方法,用图片匹配的方式进行定位操作;在此之前要先感谢公司的伟哥(称呼不大好听呀哈哈~),伟哥是我们公司的图像算法工程师,给了我很多启发和关键信息,确定了如下几个准备工作:
- OpenCV是可以实现图像匹配功能的,且Java有基于OpenCV封装的javaCV(因为伟哥用python的,具体细节需要我自己去了解,但是确认是可以的)
- 截图后图片的分辨率和原图不一致,但是这个分辨率并不影响图像识别,排除一个干扰项
- 图片的位深度是一致的,并且也不会造成匹配效果影响,又排除一个干扰项
- 图片格式对匹配无影响,只要可以加载读取即可,又排除一个干扰项
- 截取的对比图所占原图的比例越小,相对的匹配所需的精确度就越高
- 图片加载读取后会返回图片的宽/高/位深度等信息,这个为我后面的调试bug起了极大帮助
- 匹配的精确度是可以打印查看的,同两张图片的匹配精确度每次都是固定的.这个信息为我后面的匹配判定条件提供了方向和思路
整体思路:
1. 引入javaCV,封装图片匹配工具类,官网中提供了依赖地址和解释
2. 进入控件后,先截取当前图片,再用待对比图片匹配查看是否匹配成功
3. 若匹配不成功就以固定轨迹滑动一位继续匹配,若匹配成功就选中确定,继续执行后面的用例操作
如何以固定轨迹滑动可参考另外一篇博客:appium测试-如何模拟拖动屏幕下拉操作(TouchAction)–可适用于模拟常见手指滑动操作
准备工作做好了,下面正式进入实现阶段:
- JavaCV官方GitHub地址:https://github.com/bytedeco/javacv
- OpenCV中文网址:
http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/histograms/template_matching/template_matching.html
添加依赖
<dependency>
<groupId>org.bytedeco</groupId>
<artifactId>javacv-platform</artifactId>
<version>RELEASE</version>
</dependency>
封装图片匹配方法
package com.gvbrain.brainapp.api.util;
import org.bytedeco.opencv.global.opencv_core;
import org.bytedeco.opencv.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_core.*;
import static org.bytedeco.opencv.global.opencv_imgproc.CV_TM_CCORR_NORMED;
import static org.bytedeco.opencv.global.opencv_imgproc.cvMatchTemplate;
import static org.bytedeco.opencv.helper.opencv_imgcodecs.cvLoadImage;
public class JavaCVUtil {
private IplImage image;
public void load(String filename) {
//这里需要传入图片文件的路径
image = cvLoadImage(filename);
}
public boolean matchTemplate(IplImage source) {
boolean matchRes;
IplImage result = cvCreateImage(opencv_core.cvSize(
source.width() - this.image.width() + 1,
source.height() - this.image.height() + 1),
opencv_core.IPL_DEPTH_32F, 1);
opencv_core.cvZero(result);
cvMatchTemplate(source, this.image, result, CV_TM_CCORR_NORMED);
int[] maxLoc = new int[2];
int[] minLoc = new int[2];
double[] minVal = new double[2];//最小匹配度
double[] maxVal = new double[2];//最大匹配度
CvArr cvArr = result;
cvMinMaxLoc(cvArr, minVal, maxVal, minLoc, maxLoc, null);
System.out.println(maxVal[0]);
System.out.println(minVal[0]);
/*匹配条件需要根据自己的控件去设计定义,我这里因为原图每次都有内容上的些许改变,所以匹配度略有差异,就使用了匹配的区间
如果是静态完全固定不变的界面,就可以使用固定的匹配度*/
matchRes = minVal[0] == 0.8290257453918457f && maxVal[0] < 0.9615f ? true : false;
cvReleaseImage(result);
return matchRes;
}
public boolean javaCVTest(String comparisonPicPath,String originalPicPath) {
load(comparisonPicPath);//加载待对比的图片,此图片需要小于原图
boolean result = matchTemplate(cvLoadImage(originalPicPath));//校验待对比图片是否存在于原图中
if (result){//若结果匹配
System.out.println("图片匹配成功");
return true;
}else {
System.out.println("图片匹配失败");
return false;
}
}
}
实现截屏方法,获取当前截图的图片路径
public String ScreenshotAsDate(){
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd-HHmmss");
String picName = sdf.format(new Date());//获取当前系统时间作为截图名
String picOriginalPath = "D:\\QinZhen\\TestDev\\appium\\AppiumTest\\BrainAppTesting\\src\\main\\resources\\data\\image\\"
+ picName + ".png";
File screen = Driver.getInstance().appiumDriver.getScreenshotAs(OutputType.FILE);
try {
FileUtils.copyFile(screen,new File(picOriginalPath));
return picOriginalPath;
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
实现控件滚动匹配选中方法
private void swipeByCoordinate(String comparisonPicPath){
int startX = 740; int startY = 692;
int endX = 740; int endY = 630;
boolean check = false;
for (int i = 0;i<34; i++){
String originalPicPath = new BaseUtil().ScreenshotAsDate();//获取原图片文件
System.out.println(originalPicPath);
check = new JavaCVUtil().javaCVTest(comparisonPicPath,originalPicPath);
new File(originalPicPath).delete();//每次匹配完成后将截图删除
if (check){ //如果匹配到了浙江省就确定
click(A确定,null);
break;
}else {
new TouchAction<>(Driver.getInstance().appiumDriver)
.press(PointOption.point(startX,startY))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(500)))
.moveTo(PointOption.point(endX,endY))
.release().perform();
}
}
}
从原图中截取需要匹配的部分
- 原图:
- 匹配对比图:
最后看运行效果展示-滚动查找浙江省,选中确定
- 展示中包含了常见原生控件的下拉定位,具体看参考另一篇博客:appium测试-如何滑屏定位指定元素(Android)以及定位失败解决方法