Selenium图表验证实战

年后回来第二周正式开始了2018年度的第一个sprint(敏捷开发流程概览见下图),这也是转入开发团队参加的第一个sprint,前一周周五的会议上将这个sprint的任务清单安排的满满当当,当时心里还在想: 这么多需求,10天能做完吗?!


敏捷开发流程概览

周一上午每日站会上,领了个“难啃”的任务: 爬取某度页面的指数数据,某度反爬技术果真不是盖的,想通过接口拉数据?no way,不操作页面无数据请求,且接口返回的是加密的数据片段图片,在前端页面再解密组合成数据图片展示的(见下图),这肿么搞。。。。


某度指数

为什么要领这个任务?以前UI自动化测试做过那么多,有这方面基础,虽然图表的自动化测试一般都是直接跳过,不过这次想尝试挑战一下;其实这种验证或开发思路很明确,分2步:

  1. 在图表上移动鼠标指针到对应的点,出现数据浮标,截图保存;
  2. 对截图做OCR识别,提取出图片中的数据;

思路是不是很简单的感觉?可做起来就不是特么的那么回事了,中间有好几个大坑等着你跳,下面我把一些技术难点(大坑)分享出来,对做图表方面的UI自动化测试可以参考下:

  1. 第一个坑就是鼠标移动了,用过WebDriver的都知道可以用Actions,可是这个Action是基于那个元素或控件呢?
    一般第一直觉大家都会选图表对于的标签元素(如<chart>、<svg>、<graph>、<rect>、<canvas>等等),很坑,用这些标签去定位WebDriver识别不了。。。。根本找不到对应的元素(高版本没有去尝试了不知道会不会好些,我这里用的2.53.1版本),这时候就要找这个图表元素外层的第一个div标签(通常图表外面都会包一层div),鼠标移动Action的代码我也贴一下:
private void move(WebElement element, int x, int y) {
    Actions action = new Actions(driver);
    try {
        //操作
        if (x == 0) {
            action.moveToElement(element, 20, y).perform();
        } else {
            action.moveToElement(element, x, y).build().perform();
        }
    } catch (MoveTargetOutOfBoundsException e) {
        System.out.println(String.format("point(%d, %d) is out of range", x, y));
    }

    System.out.println(String.format("move the cursor to point(%d,%d)", x, y));
    sleep(1000);
}
  1. 第二个坑,移动鼠标并不能每次都完美移到位,所以就要加一个数据浮标出来了才算成功,否则就要在当前坐标的前后小范围内尝试;
move(chart, x, y);

File snapshot = snapshotAndSave(chart, genSnapshotName(x, y));
boolean isSuccess = templateMatcher.isMatch(snapshot.getPath(), templateImgFile);

int counter = 0;
int tempX = x > 0 ? x - offset : 10;

while (!isSuccess) {//如果未出现预期pop,则在区间[x-10, x+10]内重试5次
    if (counter > 4) {
        break;
    }
    snapshot.delete();//删除无效图片

    move(chart, tempX, y);

    snapshot = snapshotAndSave(chart, genSnapshotName(tempX, y));
    isSuccess = templateMatcher.isMatch(snapshot.getPath(), templateImgFile);

    tempX += offset / 2; //坐标移动offset/2位
    counter++;
}
  1. 区域截图?
    WebDriver截图通常都是截取全浏览器的,我现在就只想接图表区域那部分,how to do? 相信你们也很想知道,直接看到代码,注释很清晰了:
private File snapshotAndSave(WebElement element, String fileName) {
    WrapsDriver wrapsDriver = (WrapsDriver) element;
    File scrFile = ((TakesScreenshot) wrapsDriver.getWrappedDriver()).getScreenshotAs(OutputType.FILE);//截图整个页面
    try {
        BufferedImage img = ImageIO.read(scrFile);
        // 获得元素的高度和宽度
        int width = element.getSize().getWidth();
        int height = element.getSize().getHeight();
        // 创建一个矩形使用上面的高度,和宽度
        Rectangle rect = new Rectangle(width, height);
        // 得到元素的坐标
        Point p = element.getLocation();
        BufferedImage dest = img.getSubimage(p.getX(), p.getY(), rect.width, rect.height);
        //存为png格式
        ImageIO.write(dest, "png", scrFile);
        File file = new File(fileName);
        FileUtils.copyFile(scrFile, file);
        return file;
    } catch (Exception e) {
        e.printStackTrace();
    }
    return null;
}
  1. 第4个大坑,怎么验证那个浅黑色的数据浮标(popup)?
    这个坑比上面都要难,刚开始我想到的是用sikuli来处理,确实效果很好,识别的很准确,可是。。。我后来一问,代码规范不允许maven项目直接引用jar包,我去。。。。搜索了google半天也没找到sikuliX.jar的maven依赖方式,没办法从头再来,后面想偷点懒,想找一些现成的图片验证方案来处理,找了几个github上star比较多的项目尝试了下都不理想,跟我这个需求不匹配,那些处理都是将背景灰化,可是我就是想要验证这一层浮标的背景,灰化之后就没差异性了。所以还是得自己动手丰衣足食,先确定好思路:
    1> 截图的时候尽量只截取图表区域,排除一些无关干扰,这个可以去调整坑3中的截图坐标和高度,就会得到你满意的效果;
    2> 颜色验证方案如何做? 看过我文章的童鞋应该还记得分享过的UiAutomator2.0移动端的颜色验证方案,这次思路就是来自那里,并做了算法改进:
  • 由于浮标可以理解是一层悬浮在页面的div,那么它跟页面肯定会有交集,如果交集区域页面也有颜色就会造成很大的干扰,那么我们就直接取像素点的颜色rgb值验证就好了,千万别灰化,用那个就GG;
private boolean assertColor(int rgb) {
    //容错处理,正常浮标数据的色度在[-12170676或-11907766]=RGB(74,74,74),所以base取74
    int base = 74;
    int r = (rgb >> 16) & 0xff;
    int g = (rgb >> 8) & 0xff;
    int b = rgb & 0xff;

    boolean isMatch =
            Math.abs(r - base) * 1.0 / base < 0.2 && Math.abs(g - base) * 1.0 / base < 0.2
                    && Math.abs(b - base) * 1.0 / base < 0.2;
    return isMatch;
}
  • 考虑到很多干扰,最后我决定就只找浮标的四个顶点,遍历图片的像素点(所以截图不能太大,图片较大的话压缩一下尺寸后再遍历)验证四个顶点存在即浮标出现;
BufferedImage sourceImage = readImage(String.valueOf(sourceFile));
int sourceHeight = sourceImage.getHeight();
int sourceWidth = sourceImage.getWidth();

BufferedImage cutImage = null;
Integer counter = 1;
Map<Integer, List<Integer>> points = new LinkedHashMap<Integer, List<Integer>>();//存放顶点坐标

for (int i = 0; i < sourceWidth; i++) {
    if (counter > 4) {
        break;
    }
    for (int j = 0; j < sourceHeight; j++) {
        if (isColorRangeMatch(sourceImage, i, j, counter)) {
            if (counter > 4) {
                break;
            }
            points.put(counter, Arrays.asList(new Integer[] { i, j }));
            counter++;
        }
    }
}
  • 四个顶点的查找和验证,我这里使用了一些小算法,比如如何确定是一个顶点?干扰点如何排除?采用了构造3*3矩形点阵、矩形面积和十字交叉验证等方法;
//构造(x,y)坐标的矩形点阵
int[][] points = new int[][] { { x - 1, y - 1 }, { x - 1, y }, { x - 1, y + 1 },
                { x, y - 1 }, { x, y }, { x, y + 1 }, { x + 1, y - 1 }, { x + 1, y },
                { x + 1, y + 1 } };
矩阵点阵

如上图,一个坐标,构造对应坐标的3x3矩形点阵,红色点表示颜色匹配的,其他点颜色不匹配,如果符合这个条件就可以基本断定这是一个顶点了,但是由于界面上还有其他颜色交叉干扰,所以就还需要矩形面积(其实思路也跟上面类似,基于疑似顶点+方向构造对应的10x10矩阵,计算面积内的点都符合即进一步判断是顶点)和十字交叉验证(基于疑似顶点的x轴和y轴各上下移动一位得到4个点坐标,其中按照点的方向判断哪些点颜色符合即可判断是不是顶点);

最后按照这样跑完及匹配后切图,就会得到原图(x.png)和切出来的数据图(data*.png),如下:

效果

后面再对切出来的数据图做OCR文字识别就可以将数据都提取出来了,OCR识别这样的图片可以说是毫无压力了,要是原图那就乱惨不忍睹了,OCR文字识别这部分可以引用开源的框架或开放的OCR接口,比如百度的OCR文字识别API(有兴趣可以自己去了解了,文档很详细,步骤很简单)。

原文来自下方公众号,转载请联系作者,并务必保留出处。
想第一时间看到更多原创技术好文和资料,请关注公众号:测试开发栈

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,088评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,715评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,361评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,099评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 60,987评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,063评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,486评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,175评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,440评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,518评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,305评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,190评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,550评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,880评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,152评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,451评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,637评论 2 335

推荐阅读更多精彩内容