appium测试-操作Android非原生View(自定义)控件(如部分日期和地点选择下拉控件),基于JavaCV的图像匹配方法

appium测试中,经常遇见下拉选择控件,对于ListView类型的可以直接调用AndroidUIAutomator方法来进行定位
  • 具体方法可参考另一篇博客:appium测试-如何滑屏定位指定元素(Android)以及定位失败解决方法
但是有时候会遇见非原生的View控件,如下情况:
  • 无法定位获取控件内部选项的任何信息,也就无法用常规的id或xpath的方式去进行定位操作了
    appium测试-操作Android非原生View(自定义)控件(如部分日期和地点选择下拉控件),基于JavaCV的图像匹配方法_第1张图片
第一种比较"笨"的方法就是记住需要定位元素的固定位置或坐标,然后模拟手指操作滑动固定的次数或距离,直接选中确定,这种方法不灵活,显得不智能~

这里分享一下我的解决方法,用图片匹配的方式进行定位操作;在此之前要先感谢公司的伟哥(称呼不大好听呀哈哈~),伟哥是我们公司的图像算法工程师,给了我很多启发和关键信息,确定了如下几个准备工作:

  • 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

appium测试-操作Android非原生View(自定义)控件(如部分日期和地点选择下拉控件),基于JavaCV的图像匹配方法_第2张图片

添加依赖

		<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非原生View(自定义)控件(如部分日期和地点选择下拉控件),基于JavaCV的图像匹配方法_第3张图片
  • 匹配对比图:
    在这里插入图片描述

最后看运行效果展示-滚动查找浙江省,选中确定

  • 展示中包含了常见原生控件的下拉定位,具体看参考另一篇博客:appium测试-如何滑屏定位指定元素(Android)以及定位失败解决方法

写在最后:经验不足,能力尚弱,自己没事瞎琢磨,有大佬的话希望给点意见,能带带我就好了~~

你可能感兴趣的:(自动化测试,App自动化/Appium)