专栏首页携程技术干货 | Trip.com 智能自动化探索测试

干货 | Trip.com 智能自动化探索测试

作者简介

祥星,携程Android开发工程师,对Android自动化测试有深入的研究。

一、简介

快速的业务迭代要求快速的App发版节奏,随之而来的是质量保障压力的增大。而增大自动化程度,提升QA效率就是一种非常重要的手段,以适应快速发版的要求。

自动化探索是一种模拟用户行为,不停地在页面上点击、滑动、输入,以期望进入更多页面的一种软件测试方法。这种方法的核心在于能自动化地覆盖到常规case之外的路径,发现预期之外的问题,是众多QA手段中的一环。大家比较熟悉的Monkey是最典型的自动化探索工具,它像猴子一样在屏幕上快速地点击。Monkey的测试思路非常简单:每次从当前页面随机选择一个点(x,y)触发,这一过程一直持续直到结束。

一种典型的应用场景就是通过自动化探索跑到一些corner cases,提前发现App Crash,以降低Crash率。这种场景下对于自动化探索的核心要求就在于所能触达的页面,更进一步地是所能覆盖的软件执行路径。但 Monkey 的问题在于,屏幕上大部分区域都不可点击,其触发的大部分事件都无效,又因为是纯随机触发,事件存在大量冗余。这就导致Monkey的探索效率不高。

因此,我们提出IAET(Intelligence Android Exploration Tool)的智能自动化探索工具,一个能有效检测当前页面元素,智能化展开探索的自动化工具,以尽可能触达更多页面和软件执行路径。

本文主要内容包括:第二章UI驱动,介绍有效元素检测;第三、四章介绍探索算法及优化后的探索算法;第五章介绍工具实现;第六章介绍探索成果;第七章介绍对比实验;第八章总结。

二、UI驱动

2.1 UiAutomator简介

Uiautomator是谷歌推出的UI自动化测试框架,用于模拟点击每个控件元素,并确认输出的结果是否符合预期。UIAUtomator除了根据resource-id、content-desc、text来查找元素之外,还提供了获取页面上所有可点击元素的能力。

2.2 利用UIAutomator查找所有可点击元素

UiAutomator提供的 UiAutomation#getRootInActiveWindow() API能够获取页面上所有元素,效果等同于uiautomatorviewer查看到的页面布局树上的属性。

下面举例如何通过AccessibilityNodeInfo获取当前页面所有点击元素:

// 递归获取当前节点所有可点击的子节点
public static void getCurrentAllClickViews(AccessibilityNodeInfo nodeInfo, List<AccessibilityNodeInfo> list) {
    if (nodeInfo == null)
        return;
    if (isVisible(nodeInfo)) {
        if (nodeInfo.isClickable() && notEditText(nodeInfo)) {
            list.add(nodeInfo);
        }
        if (nodeInfo.getChildCount() != 0) {
            for (int i = 0; i < nodeInfo.getChildCount(); i++) {
                getCurrentAllClickViews(nodeInfo.getChild(i), list);
            }
        }
    }
}

通过上面的方法就能获取一个页面所有可点击的元素。

2.3 运行

我们使用 app_process 运行UIAutomator[1][2]。首先编写一个可以连接UIAutomator的JAVA程序,然后再将JAVA程序push到手机设备中,最后使用app_process启动JAVA进程,与UIAutomator建立桥梁。

使用方式如下:

第一步,创建UIAutomaion桥接类。

public class UiTestAutomationBridge {
    public void connect() {
        ...
        mUiAutomation = new UiAutomation(mHandlerThread.getLooper(), new UiAutomationConnection());
        mUiAutomation.connect();
        ...
        mUiAutomation.setServiceInfo(info);
    }


    public AccessibilityNodeInfo getRootInActiveWindow() {
        return mUiAutomation.getRootInActiveWindow();
    }
}

第二步 创建一个Main函数调用connect方法,获取RootNode。

public static void main(String[] args) {
   UiTestAutomationBridge.getInstance().connect();
   AccessibilityNodeInfo rootNode = UiTestAutomationBridge.getInstance().getRootInActiveWindow();
}

第三步将JAVA文件打成jar包,使用app_process运行jar包。

adb shell CLASSPATH='/data/local/tmp/iAET.jar' '/system/bin/app_process' '/data/local/tmp/iAET.jar' com.testing.Main

具体如何使用app_process运行JAVA程序,见参考文献[2]

三、探索算法1.0

3.1 App模型图

在理想的模型中,一个无页面状态的App模型,可以刻画成一幅模型图,其中节点代表页面,边代表事件。

以Trip.com首页为例,首页点击机票、酒店和火车按钮分别跳转到机票首页、酒店首页和火车首页。机票、酒店和火车又有自的跳转页面,这一过程一直持续,直到页面无事件为止。

研究App的遍历问题本质上就转化成研究图的遍历问题,因此我们借鉴图的深度遍历算法制定探索策略。策略如下:

  • 页面事件随机触发,触发过的事件不再触发
  • 若跳转到新页面,则优先触发新页面探索
  • 新页面事件触发完毕返回上一个页面
  • 新页面事件未触发完毕返回上一个页面,则重新回到新页面。

第四条避免随机点到返回按钮的问题。

以下面模型图为例,我们介绍App的探索过程。

  • 以A节点作为初始节点,从A节点的事件集合随机选择{e1, e2, e3}一个事件e1
  • 进入B页面。遵循规则1,以B节点作为当前节点,随机从{e4, e5, e6}选择事件e4
  • 停留B页面。遵循规则2,去掉e4事件,随机从{e5, e6}选择事件e5
  • 返回A页面。遵循规则1,B页面优先于A页面触发,重新回到B。
  • 遵循规则2,选择触发最后的e6事件
  • 进入E页面。E页面触发事件e9
  • 进入F页面。遵循规则3,返回上一个E;遵循规则3,返回B;遵循规则3,返回A。
  • 至此页面B、E、F探索完毕。

流程简化为:

pb是 press back的缩写。

3.2 算法

算法的整体思想是采用递归的方式不断地在新状态下执行探索,直到探索完成。

算法说明:

  • 算法输入是App的首页MainActivity,执行时间runningMin和一个访问过的事件集合visitedEvent
  • 第1行:MainActivity 作为当前页面S
  • 第2~3行:运行时间大于执行时间结束运行。
  • 第4行:获取当前页面下所有有效的事件集合L
  • 第5行:有效事件集合L减去访问事件集合visitedEvents得到剩余待触发事件集合L
  • 第6行:若集合L为空,则跳转至第9行,否则执行第七行
  • 第7~8行:从L随机选择一个事件触发,并记录触发后的新页面记做newState
  • 第9行:根据新页面newState和旧页面S判断是否是返回事件,若是,则从上一个页面重新回到当前页面即第10行
  • 第11行:将事件event加入访问事件集合visitedEvents
  • 第14行:当前Activity页面作为当前页面S,重复3。

四、探索算法2.0

第三章提到的App模型是一种理想的模型,一种无状态的模型。事实上,App经常出现一个页面多种状态的问题。

4.1 App状态

在3.1节我们提到App的模型图是由页面和事件构成,节点代表页面,边代表事件。实际上,我们发现一个页面可能具有多种不同的状态。下面以Trip.com的机票搜索为例来举例。

Trip.com机票不同搜索结果

上图是Trip.com搜索不同目的机票的搜索结果。搜索热门城市中国香港到北京的机票,有数十趟航班,而搜索冷门城市马来西亚的BKI(亚庇国际机场)到巴哈马的ELH(北伊柳塞拉)的机票,却没有一趟航班。同一个搜索页面,搜索的输入不同,展示的结果不同。

App模型图无法表示具有状态的模型图,因此我们引入页面状态。

页面元素

引入页面状态之前,我们先定义页面元素。

页面元素是指页面上的一个个View组件,Android一般用resource-id 或者 content-desc表示。然而一个页面元素的resource-id可能相同(如列表),所以我们必须用一个能够唯一表示页面元素的方式。

我们想到了用xpath[3]来表示页面元素。

参考维基百科上xpath的定义:/A/B/C[1]/D[resource-id='value'] C节点必须是B的子节点(B/C),同时B节点必须是A的子节点(A/B),而A是这个XML文档的根节点。而D节点是C节点的第二个元素(C[1]),D节点的属性resource-id为value,[1]是称为节点的下标。

xpath是一种结合父元素、元素类型、元素id以及元素坐标的表示方法,能够精准定位一个元素。

酒店按钮的xpath是:

//FrameLayout[0]/FrameLayout[0]/FrameLayout[0]/TextView[@resouece-id="ctrip.english.debug:id/title"]

页面状态

页面状态是指页面所处的状态,我们用页面名称(Am)+所有页面元素(Xi)的集合来表示:

不同的搜索结果页,页面名称虽然相同,但页面元素完全不同。因此用页面状态可以区分不同的搜索场景。

页面事件

引入页面状态后,一个事件用三元组来表示E_i = <Am, Xi, An>,表示页面Am触发事件Xi进入An页面,其中Am称为源页面,An称为目标页面。

App状态模型图

引入App状态后,App模型图转变成App状态模型图。其中节点代表App的状态,边代表事件。

App状态模型图能够精准表示:在一个页面状态下触发某个事件,进入新的页面状态。

4.2 算法优化--相似元素

在介绍App状态模型图探索算法前,我们先小小优化第三章的算法。在第三章我们提出新页面事件触发完毕返回上一个页面

这一条值得讨论。

在第三章,我们页面事件触发完毕的条件是所有事件都触发一遍。事实上真的如此吗?

以相册页面为例,相册页面事件数非常多,但所有事件对应一个功能(勾选)。如果用第三章的算法,随机从n张照片选择一张,直到所有照片都选择一遍,将耗费很长的测试时间。

人工测试遇到这种情况,一般采用取样+相似的思想:随机选择几个事件,测试OK。其他事件与样本事件相似,测试通过。

同样地,我们工具也引入取样+相似事件的概念。

事件相似定义

元素相似

当两个元素X_i和X_j除了下标外,其他内容完全相同,称为相似元素,记做Xi≈Xj。元素相似潜在含义是布局树中相同层级的元素可能存在相似的行为。

状态相似

当两个页面状态Si和Sj 页面名称相同,页面元素都相似时,称为相似状态,记做Si≈Sj。状态相似的潜在含义是同一个页面状态相同,其行为可能相似。

事件相似

当两个事件Ei = <Sm, Xi, Sn> 和Ej = <S'm, Xj, S'n>,具有以下特征时:

  • Sm≈S'm
  • Xi≈Xj
  • Sn≈S'n

这两个事件相似。事件相似的潜在含义是这两个事件完全具有相同的行为。

相似事件处理

一开始页面的相似事件是空集,随着事件的发生,具有相同行为的事件不断增多。当相似事件集合超过阈值时,我们认为剩余的相似元素全部相似,相似事件不再触发。

相似元素的目的是减少功能相似事件的重复触发的时间,探索更多的功能。

4.3 App状态模型探索算法

虽然App模型由页面模型改成状态模型,但本质仍然是是图的遍历问题,所以只要稍作修改即可得出App状态模型探索算法。

执行同上,不再赘述。

五、工具实现

由IAET基础服务和探索驱动两大模块组成。

基础服务模块

基础服务模块在保证保证探索正常运行的基础上,承担UI驱动的能力,主要由UI驱动和异常监控系统两部分组成。

UI驱动模块主要提供UI的检测和驱动、区分事件类型、计算页面状态和计算相似事件等能力。

异常监控系统主要保证App探索期间发生crash的情况下能够继续运行。

探索驱动

探索驱动主要将探索算法应用于实践,并结合一系列自定义规则来对App进行定制化的探索。

自定义规则

实际探索我们往往遇到各式各样定制化的需求,例如输入用户名和密码、不能进入某些页面、只想探索某几个页面。探索算法无法满足这类要求的,只能靠自定义规则来完成。

这些规则包括:预输入模块、黑名单模块、模块探索等等。

下面以预输入模块为例:

iaet-preinput.json

{
  "data": [
    {
      "activity": "activityname",
      "list": [
        {
          "contentDesc": "contentdesc",
          "resourceId": "resourceid",
          "text": "value"
        },
        {
          "contentDesc": "contentdesc",
          "resourceId": "resourceid",
          "text": "value"
        },
      ]
    },
  ]
}

预输入模块解决在哪个页面向哪些元素写入什么内容的问题,主要解决登陆注册、用户输入页面的问题。

如 iaet-preinput.json 文件所示,你可以在某个页面,向 contentDesc或者resourceid 为 XXX 的元素写入YYY值,解决那些因输入校验而阻碍探索问题。

六、探索成果

上图是Trip.com最近7个版本,IAET在发版前发现的Crash数和每次达到的页面数。CrashNumber列是发现的Crash数,ActivityNumber列是1h运行达到的页面数。

Q:为什么表格中7.5.0前后Activity Number发生两次跃迁?

7.5.0前没有相似策略,经常停留在长列表页面。7.5.0引入相似元素策略后,解决长列表问题,增加了其他页面探索的机会。

7.5.1前RN页面作为单个页面统计,7.5.1统计一个个RN具体页面的名称。

Q:如果增加探索时间,Activity Number还会增加吗?

目前测试的瓶颈在于订单页面。因为大部分没被探索的页面都是支付订单相关页面,线上包很难通过正常途径进入。除去支付订单页面,目前的页面覆盖率在80%以上。

七、对比实验

此外,我们还选择16个国内主流App作为实验对象,以APE[1]作对比工具,运行1h,以Activity覆盖率作为衡量标准展开对比实验。

实验分析

我们选择近年来学术界最新的开源、效果好的自动化测试工具APE作为对比工具。

实验的所有benchmark均是国内主流App,涵盖衣、食、住、行、教育、娱乐、新闻、运动、知识付费等各个类别,App下载量均超1亿。

最后两列是本次实验IAET和APE的实验结果,IAET在大部分App上所达到的Activity覆盖率比APE高,且有几个大幅高于APE,证明我们工具的优势。

八、总结

自动化探索提供的其实是一种基础服务能力,为性能测试、Crash检测或者页面内容检测等不同的测试目的提供测试基础。

此外,自动化探索还可以在弱网、无网和低内存等极端场景下的进行测试,以验证App的健壮性和稳定性。

参考文献

[1] T. Gu et al., "Practical GUI Testing of Android Applications Via Model Abstraction and Refinement," 2019 IEEE/ACM 41st International Conference on Software Engineering (ICSE), Montreal, QC, Canada, 2019, pp. 269-280.

[2]Android上app_process启动java进程, https://blog.csdn.net/u010651541/article/details/53163542

[3] xpath,https://zh.wikipedia.org/wiki/XPath

本文分享自微信公众号 - 携程技术中心(ctriptech)

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-28

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 干货 | 携程无线APM升级实践

    辛贵,携程无线研发总监。主要负责App基础框架研发相关工作,关注App开发框架、性能、质量、效率和新技术。

    携程技术
  • 干货 | 当你在携程搜索时,背后的推荐系统是如何工作的

    葛荣亮,携程搜索部门高级研发工程师。2015年加入携程,目前主要负责搜索平台的前端+数据挖据工作。

    携程技术
  • 干货 | 携程玩乐团队前端多端开发实践

    本文将介绍在具体业务实践中,携程玩乐团队一套代码多端复用的一些实践与经验,希望能给面对同样问题的同学提供些思路和参考。

    携程技术
  • 同域的页面间传递数据的方法汇总

    两个页面需要如果存在如下两种关系之一,才可以用 postMessage 来传递数据。

    Joel
  • 18个最佳的产品页面设计(上)

    引言:本文展示了如何让页面变得有趣个性化,展现更多细节和与众不同,让访问者轻松获得想要的信息,下面的18个产品页面设计的最佳案例不容错过。

    iCDO互联网数据官
  • 14个UI精美功能强大的Android应用设计模板

    由于狂热的开发者社区和移动设备的日益普及,Android的商业应用程序成为一个不断增长的市场。

    奔跑的小鹿
  • 2.4.1、Google Analytics高级应用——查看页面上下级

    在GA中查看上下级页面有默认参数:先前页面路径和后续页面路径,但出来的结果往往是前后相等的,错误的,如图2-48所示:

    GA小站
  • 谈谈小程序中返回上一页面逻辑

    前言:小程序页面之间的互相跳转,页面个数是开发工程师要考虑的问题。微信官方之前给出的最大页面堆栈是5,现在改为了10,如果超过最大堆栈数,会报错Maximum ...

    连胜
  • 新型Web劫持技术现身,专攻搜索引擎

    近期,安全机构截获了一例利用script脚本进行Web劫持的攻击案例,在该案例中,黑客利用一批新闻页面重置了搜索引擎页面,并将搜索结果替换为自己制作的...

    安恒信息
  • 微信小程序生命周期学习笔记-页面篇

    小程序的生命周期分三类:应用生命周期、页面生命周期、组件生命周期。现在我们来学习一下页面的生命周期。

    面向对象思考

扫码关注云+社区

领取腾讯云代金券