Appium 说说自己做的脚本录制回放工具

Kilmer · 2016年07月21日 · 最后由 白日萌 回复于 2018年05月28日 · 4597 次阅读

前言

在 TesterHome 潜水很久,基本处于看帖不发贴的状态,很好的一个社区。在这里面学习到很多知识,无论是技术还是思想。
今天决定分享一下自己做的脚本录制回放工具,听取一下大家建议。

背景

公司项目上面的原因,需要制作自动化测试的脚本录制回放工具,方便没有代码基础的测试工程师进行自动化测试。所以我这个 ATE 就只能硬着头皮来实现了。
其实我个人一直觉得脚本录制这个动作并没有什么用,一个好的测试脚本还是要靠写。毕竟录制的过程无法生成各种逻辑处理,而一个优秀的测试脚本,强健的逻辑处理是必不可少的。

言归正传,介绍一下我做的脚本录制回放工具

1. 设备识别界面

没错,ThunderSoft 就是我们公司的名字。
打开工具时,会自动识别已经连接上 PC 的手机,如果连接了多台设备,界面的 Combox 控件就会显示多台手机的 id 让你选择。
然后点击 “开始” 按钮进入下一个界面。


2. 录制界面

红色区域:
手机屏幕实时投射界面,手机的画面会实时的投射到工具上面,不用说应该很多人也知道这一块用 Minicap 就可以完成,传输效率还是很不错的。
蓝色区域:
录制脚本时,脚本的显示区域,做了文本的关键词高亮显示处理。
橙色区域:
开始录制脚本按钮
绿色区域:
脚本菜单,通过这个菜单可以导出脚本和进入脚本回放页面。


3. 输入校准界面

当点击开始录制按钮后,会进入输入校准的界面,因为需要兼容到很多不同的手机,所以需要在录制之前校准输入,测试人员需要在屏幕的任意位子点击一下屏幕完成校准。
校准完成后就可以录制脚本啦!!!!!!看校准框里面的信息,应该很多人可以猜到录制功能是依靠什么来实现的了。


4. 正在录制界面

红色框体:
里面显示的内容,就是你操作手机时录制出来的脚本,可以看到脚本有坐标形式展现的,也有通过控件相关属性展现的,
所以工具在进行录制的过程中,还是有对布局进行分析检索的动作。
实际使用过程中,TE 的反馈,录制准确率控制在 76.3%~87.2% 之间,不准确的消耗点主要还是在对输入坐标和布局文件计算上面,这部分还是有很大空间进行优化的。
点击左上角红色按钮就可以停止录制,然后通过菜单项中的导出脚本功能导出脚本了。脚本是 Python 脚本。


5. 脚本回放界面

红色区域:
要进行回放的脚本显示区域,会显示出脚本的名字,路径,测试结果,测试次数,剩余测试次数等内容。
这个 List 中的 item 在开启测试之前是可以进行拖动的,调整测试顺序。
绿色区域:
脚本回放过程中,执行信息的显示,脚本中每一个动作的执行内容和结果都会显示在这个区域。


6. 脚本导入问题

脚本的导入,是直接导入 xml 类型的配置文件,在这个文件中配置了当前测试的模式,时间,已经脚本和脚本测试的次数。
实际回放过程中也会根据配置文件中的配置数据控制测试过程。

希望社区的牛人们多多给意见。

关于测试报告,下回再讲

共收到 40 条回复 时间 点赞
Kilmer #42 · 2016年07月21日 Author

老板,加个精~

不错不错,python 脚本,我喜欢。但是感觉楼主还没说完,毕竟光录制成脚本还不够,如何断言如何处理 log 文件这些都还没说

Kilmer #40 · 2016年07月21日 Author

#2 楼 @jamesparagon 写了一半开会去了 . 要补上。

没有现成的工具吗?
另外,跳转和响应的延迟怎么搞?

喜欢网易云音乐的还真多

感觉思路也不错的样子,生成的脚本具体长什么样子,方便用 markdown 格式化,看下

期待楼主后面的分享

页面解析速度快么 用什么解析的

#4 楼 @pacerron 肯定是有现成的脚本录制工具的,先不说好不好用,只求一点能否满足业务需求,能否支持快速定制.这两点一些现成的脚本录制回放工具很难做到,再一个对回放流程的控制也极为重要 .

#9 楼 @kilmer 以我使用录制回放工具的经历来看,这种工具最大的问题是:死板,录制的操作存在一定的偏差,比如点击屏幕控件时如果和相邻的控件重叠上了,可能会识别成错误的控件,另外有的控件并没有合适的控件属性,只能用坐标来代替,这给脚本的可读性带来了负面影响。再一个问题是,回放的执行效率和录制时不一样,在某些动态场景下,延迟个 1,2 秒的操作,可能控件就变了,造成 case 失败,脚本维护工作量也很大。这些痛点不知道楼主的工具能解决吗?

可以开放出来,给大家试用试用呗

是呀是呀

你工具的录制和回放的成功率大概是多少呢?

楼主,滑动怎么处理的?

@kilmer 你这个录制工具,使用什么方法录制的呢?

思寒_seveniruby 将本帖设为了精华贴 07月22日 13:39
思寒_seveniruby 取消了精华贴 07月22日 13:39

缺乏对技术原理的介绍. 社区鼓励多 show 代码和技术原理.

Kilmer #24 · 2016年07月22日 Author

#10 楼 @jamesparagon
首先赞成你的说的,录制脚本这个动作存在偏差问题。(我一直认为录制这个功能有点华而不实。)
关于控件重叠问题:
我目前没有遇到完全重叠的情况,只要不是完全重叠就可以通过不断优化判定控件的计算方法来定位控件,目前我是这么做的,以计算器举例


用户点击 Delete 的时候怎么完成对控件的精准筛选呢?
看代码

private boolean isInWidget(String bounds,int x,int y){
    int x1=0,y1=0,x2=0,y2=0;
    Matcher matcher = pattern.matcher(bounds);
    while (matcher.find()) {
        x1 = Integer.valueOf(matcher.group(1));
        y1 = Integer.valueOf(matcher.group(2));
        x2 = Integer.valueOf(matcher.group(3));
        y2 = Integer.valueOf(matcher.group(4));
    }
    if ((x1 <= x) && (x <= x2) && (y1 <= y) && (y <= y2)) {
        return true;
    }
    else {
        return false;
    }
}       
private void findWidget(Element element,int widgetX,int widgetY){
    NodeList widgets = element.getChildNodes();
    for (int i = 0; i < widgets.getLength(); i++) {
        Element widget = (Element) widgets.item(i);
        if (widget.getAttribute("clickable").equals("true") && isInWidget(widget.getAttribute("bounds"), widgetX, widgetY)) {
            widgetList.add(widget);
        }
        if (widget.getChildNodes().getLength()>0) {
            findWidget(widget, widgetX, widgetY);
        }
    }
}
private int findBestWidget(int x,int y){
    int marginX = 0;
    int marginY = 0;
    int tmpIndex = 0;
    if (widgetList.size() > 0) {
        for (int i = 0; i < widgetList.size(); i++) {
            String bounds = widgetList.get(i).getAttribute("bounds");
            // 分析bounds
            int x1=0,y1=0,x2=0,y2=0;
            Matcher matcher = pattern.matcher(bounds);
            while (matcher.find()) {
                x1 = Integer.valueOf(matcher.group(1));
                y1 = Integer.valueOf(matcher.group(2));
                x2 = Integer.valueOf(matcher.group(3));
                y2 = Integer.valueOf(matcher.group(4));
            }
            // 计算边距
            int tmpMarginX = x - x1;
            int tmpMarginY = y - y1;
            if (i > 0) {
                if ((tmpMarginX < marginX)||(tmpMarginY < marginY)) {
                    marginX = tmpMarginX;
                    marginY = tmpMarginY;
                    tmpIndex = i;
                }
            }
            else {
                marginX = tmpMarginX;
                marginY = tmpMarginY;
                tmpIndex = i;
            }
        }
        return tmpIndex;
    }
    else {
        return -1;
    }
}

这段代码是在手机上面执行的哈,解析的数据会按照 JSON 串的形式返回给工具。

Kilmer #20 · 2016年07月22日 Author

#10 楼 @jamesparagon
另外一个问题,关于控件没有属性

不要忘了是有 xpath 的哦 ,xpath 相对路径下 bounds 也可以成为标注控件的唯一属性。

Kilmer #22 · 2016年07月22日 Author

#10 楼 @jamesparagon
第三个问题,回放的执行效率和录制时不一样
如果录制出来的脚本直接进行回放,的确是有这个问题,除非你自己在录制功能模块中加入了计时功能,能记录测试人员录制时的时序,让然后回放的时候再体现出来 。
不过我这个没加这个功能,录制出来的脚本也不会直接进行回放,而是需要进行逻辑处理然后验证脚本健壮性没有问题后,在入库投入到使中。

原理:通过 adb 实时监控读取 android 的事件并解析,翻译成对应的 action 脚本?

Kilmer #24 · 2016年07月22日 Author

#23 楼 @kasi 是这样的哈,不过读取,解析,翻译,呈现地点没放在全放在 PC 端 .

@kilmer agent 解决的吧 通过 socket 传输到 pc 端

Kilmer #26 · 2016年07月22日 Author

#25 楼 @kasi 采用 Appium bootstrap 中的方式,映射端口,启动监听,连接,输入值传输,解析定位,回传 Json 传,再翻译显示。

Kilmer #27 · 2016年07月22日 Author

#23 楼 @kilmer 本来是打算在 BSP 和输入输出子系统中做模块的,不过这种方式在各个 Base 代码中复用度太低就排除了.

@kilmer 看来你玩整机的

Kilmer #29 · 2016年07月22日 Author

#28 楼 @kasi 对的,业务做整机开发的. 疲于对应各种测试需求。

@kilmer 呵呵 辛苦 整机不好做 要求功力非凡 跨界很麻烦

这个真心不错,如果详细点就好了,期待下面的内容,

我用按键精灵是不是显得太 out 了(手动哭笑不得表情)

楼主能说一下技术原理和 show 一下代码吗?

...你这是简介啊 可以介绍原理吗?用了什么工具?录制的原理是什么?为什么要适配?获取屏幕计算相对坐标不可以吗?GUI 是 py 写的吗? ...要不放出来大家一起用吧😯

惊为天人

这个思路很好呀~ 楼主能否把代码放出来,大家一起用么?

#23 楼 @kasi @kilmer 如果是监控 android 事件,有的手机,点击返回按键,并不会返回有效的类似于 KEY_BACK 的字符,那么该如何判断点击了返回键呢?

界面对比吧

#38 楼 @kasi 是在回复我的问题吗?界面对比?能否详细点呢?

没有试用版吗 小哥?

仅楼主可见
仅楼主可见
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册