软件学报  2020, Vol. 31 Issue (8): 2508-2529   PDF    
一种基于录制/重放的Android应用众包测试方法
曹羽中1,2 , 吴国全1,3,4,5 , 陈伟1,3,4,5 , 魏峻1,3,4,5 , 黄涛1,3,4 , 王溯2     
1. 中国科学院 软件研究所 软件工程技术研究开发中心, 北京 100190;
2. 北京城市学院 信息学部, 北京 100083;
3. 计算机科学国家重点实验室(中国科学院 软件研究所), 北京 100190;
4. 中国科学院大学, 北京 100049;
5. 中国科学院 软件研究所 南京软件技术研究院, 江苏 南京 211169
摘要: 随着Android设备的流行和普及,Android生态系统的碎片化问题越发严重.为了确保应用质量,Android应用需要在多种设备上进行测试.为了应对大量重复机械的测试工作,学术界和工业界提出了众多跨设备的测试方法,但目前的方法还有较多的局限性:(1)手工编写设备无关的测试脚本耗时且容易出错;(2)现有录制/重放方法生成的测试脚本在跨设备重放时会出现各种问题,导致重放失败;(3)由于缺少足够的Android设备,应用难以在大量不同类型的设备上进行测试;(4)现有的测试方法由于缺少应用特定的领域知识,无法生成有效的用户输入,导致测试覆盖率不高.基于以上原因,大量的应用在没有经过充分测试后发布,兼容性问题频发.针对以上问题,提出一种基于录制/重放的Android应用众包测试方法,并实现了原型工具AppCheck.AppCheck收集众包用户和设备交互时所产生的事件序列后,将其转换为平台无关的测试脚本,可直接在众包用户的设备上进行重放.在重放期间,AppCheck收集各种测试相关数据(例如截图和布局信息)以检测兼容性问题.实验结果表明,AppCheck能够有效地完成跨设备录制/重放以及兼容性问题的检测,弥补了当前方法的不足.
关键词: 安卓    众包测试    碎片化    自动化测试    录制    重放    
Crowdsourcing Test Method for Android Applications Based on Recording/Replay
CAO Yu-Zhong1,2 , WU Guo-Quan1,3,4,5 , CHEN Wei1,3,4,5 , WEI Jun1,3,4,5 , HUANG Tao1,3,4 , WANG Su2     
1. Technology Center of Software Engineering, Institute of Software, Chinese Academy of Sciences, Beijing 100190, China;
2. Department of Information, Beijing City University, Beijing 100083, China;
3. State Key Laboratory of Computer Science(Institute of Software, Chinese Academy of Sciences), Beijing 100190, China;
4. University of Chinese Academy of Sciences, Beijing 100049, China;
5. Nanjing Institute of Software Technology, Institute of Software, Chinese Academy of Sciences, Nanjing 211169, China
Abstract: It is well known that the fragmentation of Android ecosystem has caused severe compatibility issues. Therefore, for Android apps, cross-platform testing (the apps must be tested on a multitude of devices and operating system versions) is particularly important to assure their quality. Although lots of cross-platform testing techniques have been proposed, there are still some limitations: 1) It is time-consuming and error-prone to encode platform-agnostic tests manually; 2) Test scripts generated by existing record/replay techniques are brittle and will crash when replayed on different platforms; 3) Developers, and even test vendors have not equipped with some special Android devices; 4) Due to the lack of specific domain knowledge, the existing test methods cannot generate effective user inputs, resulting in low testing coverage. As a result, apps that have not been fully tested, will lead to many compatibility issues after releasing. To address these limitations, this study proposes AppCheck, a crowdsourced testing service for Android apps. To generate tests that will explore different behavior of the app automatically, AppCheck crowdsources event trace collection over the Internet, and various touch events will be captured when real users interact with the app. The collected event traces are then transformed into device-independent test scripts, and directly replayed on the devices of real users. During the replay, various data (e.g., screenshots and layout information) will be extracted to identify compatibility issues. The empirical evaluation shows that the proposed AppCheck is effective and improves limitations of the state-of-the-art.
Key words: Android    crowdsourced testing    fragmentation    automated test    recording    replay    

随着移动互联网技术的发展, 未来互联网重心从传统的个人电脑转移到了新一代移动设备上, 智能手机开始被广泛使用.我们的日常生活, 如购物、银行和旅行等活动都离不开手机应用程序.特别是Google公司推出移动智能设备操作系统Android之后, 各大主流手机制造商大规模地推出Android智能手机, Android设备种类从2014年的18 796[1]增长到2015年的24 093[2].在2017年Google I/O大会上, 谷歌宣布, 每月活跃安卓设备数量超过20亿[3].由于Android系统版本更迭频繁, 同时为了吸引用户并在竞争中保持优势, 不同的设备厂商会对Android系统进行定制, 从而导致Android生态系统的碎片化.碎片化现象给Android应用的测试带来了巨大挑战, 应用开发者需要在多种不同类型、不同版本设备上进行大量重复机械的测试工作, Android应用的自动化测试技术因此成为研究热点.

在学术界, 虽然目前已有很多针对Android应用的自动化测试技术和工具[4-6], 然而根据最近的实证研究[7], 这些技术的测试覆盖率仍然很低.这是因为大量的应用程序是为具备特定领域知识用户开发的, 如果不具备这些领域知识, 将无法触发某些特定的交互场景.在工业界, 已有多种Android应用自动化测试框架被广泛使用, 如Android Espresso[8], Robotium[9], UIAutomator[10], Appium[11]等.这些框架可以跨设备自动执行测试用例(Appium甚至支持测试Hybrid App).但是不足在于, 需要测试人员掌握特定的编程语言, 并且手工编码会消耗测试人员大量的精力.

用户交互行为录制/重放是一种常见的Android应用测试方法[12, 13].测试人员通过记录收集用户与被测应用的交互事件序列, 并在不同设备进行重放, 以验证被测应用的功能与规约是否一致[14].这种方式可以用于Android应用的兼容性测试, 实现测试成本的降低和测试效率的提升[15, 16].现有的录制/重放技术[12]利用Android底层接口获取系统日志, 进而形成测试脚本, 具有不侵入应用内部、可以描述多应用交互等优点, 但得到的原生事件流脚本接近于机器码, 不便于测试人员进行编辑修改; 并且生成的测试脚本难以实现跨设备重放, 缺乏重用性.尽管相关工作尝试采用成线性比例缩放机制实现跨设备屏幕尺寸和分辨率的适配[13], 但方法适用性非常有限, 当应用的界面布局随着屏幕旋转或屏幕尺寸、分辨率发生变化后, 该方法会失效.这导致生成的测试脚本却不够健壮, 在其他测试设备平台上运行时会崩溃.

区别于传统的GUI程序测试, Android生态系统的碎片化要求应用需要在不同类型、不同版本的Android设备上进行测试, 以保障应用的健壮性.但是在当前Android设备快速更新升级的情况下, 应用开发者甚至专业的测试厂商[17]都不可能购买所有需要测试的Android设备.为了解决这一问题, 众多测试厂商(如uTest[18]和Testin[19])使用了一个新兴的技术——众包测试[20]来测试移动应用.在众包测试中, 拥有不同移动设备和使用环境的真实用户测试同一应用软件.如发现Bug, 用户将根据Bug的严重程度得到奖励.然而, 这样的过程仍然需要大量繁琐的手工工作, 同时, 用户报告的Bug质量得不到保证[21].通常情况下, Bug在开发人员和用户之间多次沟通后才能得到确认.

针对以上不足, 本文提出了一种基于录制/重放的Android应用用户交互行为跨设备众包测试方法, 并基于该方法实现了录制重放工具AppCheck.AppCheck具备以下3个特点.

(1)   基于测试框架STF[22], AppCheck允许测试人员通过PC或智能手机浏览器和真实用户的设备进行交互, 并在交互过程中捕获用户的各种Touch事件.

(2)   将设备相关的Touch事件转换为设备无关的抽象用户动作, 并基于Android辅助功能[23]支持直接在参与众包测试的用户真机上进行重放.

(3)   通过收集在不同设备上重放产生的数据, 设计兼容性问题检测算法, 除了识别常见的兼容性问题之外, 还能识别不同设备重放时产生的性能问题(如按钮长时间不响应点击事件).

本文提供的方法还有以下优势:支持一次录制, 处处运行.测试人员可以在某一特定的设备捕获用户的操作序列后, 将操作序列转换为设备无关的测试脚本后, 在其他设备进行重放; 此外, 本文介绍的方法不需要侵入应用进行插桩或对应用进行重签名.在录制阶段, AppCheck仅需要用户通过浏览器操作应用, 便可以实现操作序列的收集工作.在重放阶段, AppCheck会负责将转换后的设备无关操作序列分发到需要测试的用户设备上, 并完成重放.

为了实现以上目标, AppCheck在实现上需要解决如下问题:首先, 当用户与App交互时, 产生的Android系统底层操作日志流依赖于特定平台, 不能针对具有不同屏幕尺寸和分辨率的设备进行跨设备重放.为了解决这一问题, AppCheck设计转换算法将捕获的低级事件抽象转化成平台无关的用户动作(如单击一个特定的资源ID按钮).同时, 为了定位相应的UI组件, 设计了3种不同的UI组件定位方式:资源ID、属性和基于XPath的定位器, 这些方式独立于设备的屏幕大小和分辨率, 可以跨设备对UI组件进行定位.

为了检测应用在具有不同屏幕尺寸和分辨率设备上的兼容性问题, AppCheck首先比较不同设备是否具有相同的UI组件布局.当组件布局相同时, AppCheck通过对比不同设备的截图来进一步检测是否存在兼容性问题.除此之外, AppCheck通过比较不同设备上用户操作的执行时间来识别性能相关的兼容性问题.本文的主要创新与贡献如下.

(1)   设计并实现了一种基于录制/重放的Android应用众包测试方法, 并实现了工具原型AppCheck.该工具通过众包测试的方式收集用户操作序列, 并可直接在用户设备上重放所收集的操作序列;

(2)   设计了一种转换算法, 支持将收集到的设备相关Touch事件转换为设备无关的抽象用户动作, 并基于辅助功能服务完成跨设备重放;

(3)   设计了一种Android应用兼容性问题的检测机制, 以识别Android应用在不同设备重放时所发生的兼容性问题.

本文第1节给出本文工作的研究动机及示例.第2节给出本文工作整体方法的详细说明.第3节介绍本文提出方法的具体实现细节.第4节通过对各大应用商店挑选出的100个应用进行评估, 以验证本文提出方法的有效性.第5节介绍本文方法的局限性.第6节和第7节是相关工作以及总结.

1 研究动机

我们以两个常用的移动应用为例子, 说明现有录制/重放方法在跨设备重放时存在的不足.滴滴出行[24]是目前非常流行的移动应用, 为用户提供了一个覆盖出租车、专车、快车、顺风车、代驾及大巴等多项业务在内的一站式出行平台.图 1分别是滴滴出行((v5.1.20_284)在三星Note5(Android 6.0)和红米4X(Android 6.0)上运行的截图.可以发现, 代驾菜单位置在两个设备上有很大的差别:三星Note5中, 代驾菜单可以正常显示(如图 1(a)所示), 而代驾菜单在红米4X中不能完全显示(如图 1(b)所示), 甚至与三星Note5中代驾菜单的位置不对应.现有的记录/重放技术[12, 13]无法处理这一场景, 因为这些技术基于界面控件元素在跨设备时成线性比例缩放的假设.然而, 这样的假设在该应用中并不成立, 如图 1所示, 捕获的Touch事件(如点击代驾菜单)从三星Note5转换到红米4X上会失败, 经过我们的实验发现, 存在大量的移动应用界面控件元素在跨设备平台时并不成线性比例缩放.

Fig. 1 Didi chuxing app (result of equal scale conversion of designated driving menu) 图 1 滴滴出行app(代驾菜单等比例转换结果)

京东Android客户端[25]也是目前流行的一个移动应用程序.它是一款专为Android设计的手机购物软件, 拥有商品购买、查询订单、订单跟踪、商品晒单等一站式购物平台.能够在多种Android设备(如华为、三星等)运行流畅.然而对于某些特定设备, 例如在安装了小米MIUI8的设备上运行京东Android客户端(v7.0.6)时, 却有明显的卡顿现象[26].为了发现类似的兼容性性能问题, 购买所有需要测试的设备是不可实现的, 一种可行的办法是使用众包测试方法, 邀请拥有各种不同Android设备的用户, 直接在他们的设备上利用进行测试.

综上, 现有的Android录制/重放技术[12, 13]假定UI控件在不同设备上成线性比例缩放, 难以适用于跨设备重放, 且依赖于Android SDK工具包提供的adb工具, 需要设备在测试前先和服务器端连接, 无法支持众包测试.同时, 现有的兼容性问题检测技术[27-29]只能识别一些布局以及用户操作行为相关的问题, 没有提供与应用运行性能相关的兼容性问题检测机制.

2 整体方法

为了解决现有技术框架的不足, 我们提出了一种基于录制/重放的Android应用众包测试方法, 并开发了相应的工具原型AppCheck.AppCheck借助STF框架[22]捕获真实用户和App交互时所产生的事件序列, 并将这些事件序列转换成设备无关的中间语言测试脚本.该脚本可在参与众包测试的真实用户设备上直接进行重放, 在重放过程中, AppCheck收集截图和页面布局等信息, 并使用检测算法进行兼容性问题的检测, 最终向开发者提供可视化检测报告.图 2展示了AppCheck的总体结构, 主要包括4个步骤:事件序列收集、用户行为抽象, 跨设备重放和兼容性问题检测.接下来, 将介绍每一个步骤的具体实现细节.

Fig. 2 AppCheck overview 图 2 AppCheck总体结构

2.1 Android操作序列的收集

为了便于收集众包测试用户与App的交互事件序列, AppCheck借助STF框架为用户提供了Web接口, 通过该接口, 用户可与被测试应用进行交互.在交互过程中, 各种用户操作将被捕获并记录.在该阶段, AppCheck还负责收集每一个操作后应用的界面布局和UI组件信息.在用户交互行为抽象阶段, 通过分析收集到的界面布局和UI组件信息, AppCheck将捕获到与设备相关的日志流信息转换为与设备无关的用户操作序列.

为了支持事件序列的收集, AppCheck主要由3部分组成:被测Android设备平台、后端服务器以及Web客户端.被测Android设备平台可以是真实的Android设备, 也可以是Android模拟器.如果是真实的Android设备, 后端服务器将通过互联网远程连接待测试设备; 如果是模拟器, 后端服务器将自动化配置模拟器参数并在后端服务器上运行模拟器.

后端服务器负责管理所有可连接的设备和模拟器.通过Socket连接, 后端服务器持续不断向Web客户端发送被测试应用的运行状态截图和UI信息.同时, 后端服务器还负责将用户的操作序列从Web客户端传递到待测试设备.

Web客户端可实时在浏览器显示被测试应用在测试设备上运行的JPEG格式截图, 同时将用户在浏览器上的操作序列发送到测试设备上执行.Web客户端可以运行在笔记本电脑、台式机、甚至移动智能设备上.后端服务器和Web客户端的实现都基于开源的Open STF[22]框架, 该框架整合了minitouch库[30], minitouch提供了一个Socket接口用来收集Android设备上的多点触控事件.STF框架支持Android API Level 10以上的设备且不需要Root权限.通过整合该框架, AppCheck能够支持触屏手机常规的手势操作, 如MultiTouch和Swipe等手势操作.

当用户通过Web客户端操作被测应用的界面时, Touch事件和坐标信息会被minitouch捕获, 并通过网络传输到Android设备上.在minitouch中, 一个Touch事件被定义为

$ type<contact ><x><y><pressure> . $

其中, type描述了Touch事件的类型, 它的取值可以是d(表示touch down事件), m(表示move on事件)以及u(表示touch up事件); < contact>表示多点触控手势操作中手指的标示值; < x> < y>存储被触碰的坐标; < pressure>表示该触控事件的力度.其中, < x> < y>值和 < pressure>值均可为Null.

除了Touch事件, minitouch还有一些其他类型的事件.例如, c是一个提交事件, 它表示提交当前操作序列的一次提交.在同一个提交中, 同一根手指不能有一个以上的d, mu事件.当c事件被提交到测试设备后, minitouch将执行在屏幕上触发当前的操作集合.例如, 当测试人员出在Web客户端触发一个touchdown事件后, 生成操作序列 < d 0 10 10 50>, < c>, 表示一根手指以50的压力在屏幕坐标(10, 10)处按下.该操作序列将在提交事件c到达后被执行.

然而, 捕获的Touch事件序列包含设备相关的坐标信息, 无法直接在不同分辨率和不同屏幕尺寸的设备上重放.为了解决这一问题, 在此阶段, AppCheck还保存Touch事件发生后被测应用的UI结构信息.借助于收集的UI结构信息, 设备平台相关的Touch事件序列将被转换成为平台无关的用户操作序列.

在Android平台上, 应用的UI组件结构信息可以通过Hierarchy Viewer[31]以及UIAutomator[32]等工具获取.因为Hierarchy Viewer需要在Root模式下工作, 我们选择UIAutomator作为UI组件结构信息的获取工具.然而, UIAutomator服务默认将UI组件信息储存在外部存储设备上, 同时, 生成记录UI组件信息的文件也需要数秒, 无法达到实时收集UI组件的目标.为了解决以上问题, 我们修改了UIAutomator, 精简了UI组件属性的数量, 修改后的UIAutomator只记录UI组件的资源ID和index、文本内容、坐标范围等信息.在修改后, UI组件信息文件的生成仅仅需要数百毫秒.

2.2 用户行为抽象

为了支持跨设备重放, AppCheck将用户Touch事件序列转化为可在各种类型Android设备上执行的平台无关中间脚本语言, 其文法定义见表 1.

Table 1 Syntax of device-agnostic representation 表 1 中间语言语法

中间脚本语言包含用户操作以及所操作的UI组件(通过selector定位).目前, AppCheck定义了7种用户的操作:Click, LongClick, Input, Swipe, Scroll, MultiTouch以及EntityKey(实体键).为了精确识别平台无关的操作, AppCheck定义用户的每个操作以Press事件开始, 中间包括0个或多个Move事件, 最终以Release事件结束(对于MultiTouch这样的手势操作, 会有一个Press事件、一个以上的Move事件和一个Release事件组成).同时, 对每个操作定义了与之相对应的时间自动机.下面给出AppCheck所使用的时间自动机定义.

定义 1. AppCheck中使用的时间自动机是一个六元组M=(S, Σ, S0, C, E, F), 其中, S为一个有限状态集合; Σ为集合{c, d, m, u}, 其中, c, d, m, u是minitouch中上定义的4种事件的类型; S0S为初始状态; C是表示时钟集合, 该集合的元素个数是有限的; ES×E×Φ(C)×2C×S是边的集合, 其中, Φ(C)表示时钟约束的集合.边 < S0, d, δ, S1>表示当输入字符串为d时, 从状态S0到状态S1的转换.δ是时钟集合上的一个时钟约束, 即δΦ(C), 当δ满足时, 转换才能发生, 其中, δ可以为空.FSS的终止状态.

根据定义1, Click与LongClick操作的时间自动机如图 3所示, 其中, eLapsed是时钟集合中的一个元素, eLapsed < 500ms和eLapsed>500ms是时钟集合上的两个时钟约束, eLapsed用于记录从S0开始到达S2状态所经历的时间.

Fig. 3 Click and LongClick event timed automata 图 3 Click和LongClick事件时间自动机

根据第2.1节中minitouch对Click事件的定义, S0S2分别对应Press事件和Release事件的开始.根据Android系统默认的设定, 当Press事件和Release事件操作码的时间间隔大于500ms[10]时, AppCheck会认定当前操作序列是一个LongClick操作.例如下图 3中, S5为Click事件序列终点, S6为LongClick事件序列终点.

相对于识别Click操作, Input事件的识别更加复杂.这是因为对于触屏智能手机, 用户的输入都是依靠软键盘, 这导致Input事件本身也是一个点击事件, 但不同在于点击的目标是软键盘.为了识别Input事件, 当用户每次点击可输入的UI组件(如EditText)后, AppCheck会检验可输入的UI组件是否成为屏幕焦点, 如果用户点击可输入的UI组件且其text属性发生变化, AppCheck有限自动机将判定当前为一个Input事件, 通过查找UI结构树(Android的UI组件结构信息是以树形结构组织)获取组件的Text属性, 当组件失去焦点后, AppCheck会判定输入完成, 读取所操作组件的Text属性值作为最终输入.

Input事件的识别算法如下.

算法1. Input事件的识别算法IEI(input event identify).

输入:ESi:操作事件control:UI组件.

输出:InputStmt:平台无关的字符串输入语句.

01:      If typeof(ESi) is Click event then

02:      If typeof(control) is android.widget.EditText then

03:indexcontrol.index

04:resourceidcontrol.ResourceId

05:      While control is forced do

06: bufStringcontrol.text

07:          End While

08:        End If

09:      End If

10:      InputStmtInputresourceid indexbufString

11:return InputStmt

IEI算法的主要输入有:操作事件ESi, UI组件control.算法输出是平台无关的字符串输入语句InputStmt.在IEI算法的第1行、第2行中, 首先判断当前操作事件ESi是否是Click事件且UI组件类型为android.widget. EditText控件, 如果满足这两个条件, AppCheck将会认定当前操作为输入操作.IEI算法的第3行、第4行负责记录输入内容以及控件ResourceID, index等信息.在算法的第5行~第11行中, AppCheck根据当前控件是否获得焦点来判断输入字符串是否输入完毕.当前控件失去焦点后, 算法的第10行、第11行会判定输入结束并返回平台无关的输入语句InputStmt, 以供重放使用.

Scroll事件主要应用于控件android.widget.ScrollView, 其自动机以(d c)开始, 而后伴随着多个(m c), 最后以(u c)结束.根据定义1, Scroll事件序列的时间自动机如图 4所示(其中, S6代表Scroll或Swipe事件识别成功).识别Scrollevent后, AppCheck将根据起始点与终点位置关系来判断用户手指的移动方向, 进一步将Scroll划分为两类:Scroll-forward以及Scroll-backward.

Fig. 4 Scroll and Swipe timed automata 图 4 Scroll和Swipe时间自动机

Swipe事件的自动机与Scroll事件相同, 区别是当前事件序列是否应用于android.widget.ScrollView控件:如果是, AppCheck会判定当前事件为Scroll事件; 否则, 将判定当前事件序列为Swipe事件.同样地, 根据用户手指的移动方向, AppCheck进一步将Swipe划分为4类:Swipe-up, Swipe-down, Swipe-right以及Swipe-left.

MultiTouch事件的触发需要两根手指同时操作.为了识别MultiTouch事件, Minitouch规定不同手指的Press事件通过字符串d0或d1标示、Move事件通过字符串m1或m2标示、Release事件通过字符串u1或u2标示.不同手指事件的字符串d0, m0, u0和d1, m1, u1可以连续被接收, 也可以在提交一个c字符串后交替被接收.根据以上定义, 我们给出了MultiTouch事件的自动机, 如下图 5(其中, S23代表MultiTouch事件识别成功)所示, AppCheck将MultiTouch事件抽象为Press, Move, Release这3个阶段, 分别对应图 5的左侧部分、中间部分以及右侧部分.自动机接收到字符串d0或d1后, 开启MultiTouch事件并进入Press阶段.根据上文所述的规则, 字符串d0和d1可以连续被接收, 也可以在提交一个c字符串后交替被接收, 因此, 从状态S0到状态S7共有4条路径.同理, 如图 5的中间部分和右侧部分所示, 在Move阶段的状态S7S15以及Release阶段的状态S15S23之间, 同样也有4条路径.不同之处在于, 因为MultiTouch事件包含一个以上的Move事件, 所以当S15在接收到字符串m0或m1后, 需回到状态S8S9, 继续停留在Move阶段以便继续接收新的Move事件字符串.当状态S15接收到字符串u1或u2后, 自动机将进入Release阶段, 经过上文所提到的4条路径后到达终止状态S23.根据两个手指间的距离变大或变小, AppCheck将MultiTouch事件进一步划分为zoom-in以及zoom-out, 分别代表多点触屏中放大手势以及缩小手势.

Fig. 5 MultiTouch event timed automata 图 5 MultiTouch事件时间自动机

为了识别与以上操作序列的相关UI组件, AppCheck通过遍历Android UI树直到所遍历组件坐标在事件触碰的屏幕范围内(当有多个组件符合条件时, 根据组件的text, clickable, editable等属性进一步筛选组件).当成功遍历到满足条件的组件后, AppCheck会记录下组件的ResourceID, index, class, type, text, coordinates, clickable以及Android UI树中此叶结点的XPath等作为selector定位器.

selector用于精确地标识与操作相关联的UI元素, 并且独立于设备的屏幕大小和分辨率.为了唯一标识并查询到目标UI组件, AppCheck定义并应用了3种定位方式:ResourceID加index定位器、属性定位器和XPath定位器来识别与用户操作相关联的UI组件.Resource ID加index定位器存储UI元素的ResourceID以及该UI元素的index.属性定位器基于两个属性标识一个元素:元素的类和元素所显示的文本.XPath定位器根据UI元素在第2.1节中提取的Android UI树中的位置标识一个元素.我们的方法不使用ResourceID作它唯一的定位器, 原因有两个:首先, 在Android框架中, 不是每个资源元素都会有ResourceID(有些UI元素的ResourceID为空); 其次, 在一棵Android UI树中, 可能会有ResourceID重名的情况.这3种不同的定位方式将应用在方法的重放阶段中, 用来唯一识别与用户操作序列相关联的UI组件.

2.3 跨设备重放

为了重放第2.2节中记录的操作序列, 一种可行的方法是将操作序列所生成的测试用例转换成可以被现有Android平台测试框架执行的测试脚本(如Android Espresso[8]、Robotium[9]).然而, 这种方法需要被测试设备连接到后端服务器后, 再运行adb命令执行测试用例.很显然, 这种方法不适用于众包测试的场景.

为支持在众包测试环境中完成平台无关的操作序列(第2.2节中抽象出的操作序列)重放, 我们开发了一个基于Android平台辅助功能[23]的重放应用程序.该应用程序主要完成3个任务:(1)为测试用例搭建一个适当的运行环境; (2)执行测试用例; (3)将收集到的运行数据传送到AppCheck服务器端.接下来, 将从以上3点介绍AppCheck跨设备重放的具体实现.

●   运行环境搭建

启动该应用后, 测试人员可以从AppCheck提供的列表中下载测试脚本和被测应用, 重放应用程序会自动安装被测应用.安装完成后, AppCheck将启动被测应用并执行测试脚本.

●   执行测试用例

AppCheck解释器将逐行读取测试脚本, 直到所有脚本语言执行完毕.当重放失败时, 解释器会定位重放失败的语句并生成重放失败报告.

脚本语言的执行主要使用Android Accessibility Service所提供的API实现, 通过使用AccessibilityService测试设备, 无须连接adb即可完成操作序列重放.例如, 它可调用findAccessibilityNodeInfosByID(∙) API通过ResourceID定位UI组件, 也可以通过调用findAccessibilityNodeInfosByText(∙), 通过文本寻找UI组件, 并根据UI组件的属性值来判断搜索到UI组件是否符合要求, 如符合要求, AppCheck将调用performAction(∙) API去执行相关操作.如果上述方法无法找到符合要求的UI组件, AppCheck会根据XPath定位器去遍历UI树直到发现与操作序列相关的唯一UI组件, 然后触发相应的操作.

在重放过程中, 可能会出现某个操作导致某个Activity或View被重新加载, 但在新的Activity或View没有完全加载之前, 下一个操作被调度执行的情况.为了避免这种情况, 现有的大多数技术和工具(例如Robotium[9], Uiautomator[10])插入一些等待原语(例如Waitfortext、Waitforactivity).然而, 这些等待原语的插入需要特定领域知识, 自动化完成这些等待原语的插入非常困难且容易出错.

为了解决这一问题, AppCheck监听了由Android系统维护的系统级事件:State-changed事件(State-changed事件触发时, 某个Activity或View会被加载).当State-changed事件被触发时, 我们认为某个Activity或View将会被加载, AppCheck将等待2 000ms(Google官方给定的执行时间标准)后, 调度执行下一个操作.

●   运行数据传送

在重放每个操作之前, AppCheck将收集当前运行应用程序的UI结构信息以及应用截图.每个操作响应时间(响应时间的计算方法将在第2.4节中介绍)和系统日志也同时被记录下来.这些运行数据将在重放完成后, 被传送到AppCheck服务器端.

2.4 兼容性问题检测

根据从第2.3节收集到的数据, AppCheck从功能、性能和显示这3个方面检测应用跨设备重放的兼容性问题.接下来将从这3个方面介绍AppCheck兼容性检测过程的具体实现.

●   功能

功能兼容性问题检测分为异常识别和Android UI树匹配两个部分, 分别负责检测异常信息和UI结构.

●   性能

为了检测跨设备重放时发生的性能问题, AppCheck监听了由Android系统维护的系统级事件:Content- changed事件.当Content-changed事件触发时, 当前执行界面的内容会发生改变(例如, 用户使用软键盘输入后, Activity内某个EditText内容发生变化).通过监听Content-changed事件以及参考现有的研究工作[33, 34], 我们对操作序列跨设备重放的响应时间给出了如下定义.

定义 2(AppCheck操作跨设备重放响应时间).本文定义操作序列跨设备重放的响应时间为从操作被AccessibilityService调度执行后, 到新窗口第1个Content-changed事件发生后的时间间隔.

根据定义2, 在重放的每一个操作之前, AppCheck会通过在调度执行Click, LongClick, Input, Swipe, Scroll, MultiTouch以及EntityKey等操作函数中嵌入System.currentTimeMillis(∙) API完成对每个操作执行开始时间的记录, 并监听此后第1个Content-changed事件的触发时间, 并以此为依据计算出不同设备上每个操作的响应时间.这些运行数据将被收集并发送到后端服务器, 用于检测兼容性问题.

●   显示

为了全面检测跨设备重放时发生的显示问题, 我们将显示检测分为3个步骤:Structure check检测、OCR (optical character recognition, 光学字符识别)检测、Color Histogram(颜色直方图)检测.AppCheck兼容性问题检测内容列表见表 2.

Table 2 Compatibility issue detection range of AppCheck 表 2 AppCheck兼容性问题检测范围

根据上文所述的兼容性问题检测范围, AppCheck将从3个方面、5个子过程检测兼容性问题, 具体实现如算法2所示.

算法2. 兼容性问题检测算法CID(compatibility issue detection).

输入:RDModel:录制时收集到的参考设备运行状态信息集合, 集合中每个元素包含log(日志)、screenshot(截图)、tree(Android UI树)等信息; TDModel:重放时收集到的测试设备运行状态信息, 与RDModel数据结构相同.

输出:CIDReport兼容性问题检测报告.

01:Begin

02:      CIDReport=null

03:      //异常识别

04:Foreach line in tdmodel.log

05:            If line.indexof(“Exception”)!=null then

06:              CIDReport.add(Exception-CID(line))

07:            End If

08:        End For

09:            //Android UI树检测

10:            //定义检测属性集合

11:prop← {“package”, “class”, “resource-id”, “selected”, “focused”, “index”, “content-desc”, “password”,

12:          “long-clickable”, “scrollable”, “focusable”, “clickable”, “checkable”}

13:For i=0; i < RDModel.size; i++

14:          rdmodel=RDModel.get(i)

15:          For j=0; j < TDModel.size(∙); j++

16:            tdmodel=TDModel.get(j)

17:Foreach child element n1i in rdmodel.tree

18: Foreach child element n2i in tdmodel.tree

19:              If n1i.name==n2i.name then

20:                matchNode, matchProp← 0

21:                Foreach property p in prop

22:                  If n1i.p=n2i.p or (n1i.p.scrollable=true or n2i.p.scrollable=true) then

23:                      matchProp++

24:                  ElseIf n1i.p.scrollable=fasle and n2i.p.scrollable=fasle then

25:                      CIDReport.add(AndroidUITreeCheck-CID(n1i, n2i))

26:                  End If

27:                End For

28:                If matchProp=prop.size then

29:                  matchNode++

30:                End If

31: End If

32:              End For

33:            End For

34:            If matchNode=rdmodel.tree.size or then

35:              //Structure check检测阶段

36:              bool ifStructMatch=StructureCheck(rdmodel, tdmodel, CIDReport)

37:If ifStructMatch then

38:              //Ocr check检测阶段

39:bool ifOcrMatch=OcrCheck(rdmodel, tdmodel, rdmodel.screenshot, tdmodel.screenshot)

40:                If ifOcrMatch=false then

41:                CIDReport.add(OcrCheck-CID(rdmodel, tdmodel, rdmodel.screenshot, tdmodel. screenshot))

42:              End If

43:              //Color Histogram检测阶段

44:              bool ifColorHistogramMatch=ColorHistogramCheck(rdmodel.screenshot, tdmodel. screenshot)

45:              If ifColorHistogramMatch=false then

46:            VCIDReport.add(ColorHistogramCheck-CID(rdmodel.screenshot, tdmodel.screenshot))

47:                ImageCompare(rdmodel.screenshot, tdmodel.screenshot)

48:            End If

49:            If ifColorHistogramMatch and ifOcrMatch then

50:              CIDReport.add(rdmodel+“×”+tdmodel+“Pass”)

51:            End If

52:           Else

53:              ImageCompare(rdmodel.screenshot, tdmodel.screenshot)

54:           End If

55:          End If

56:          //性能检测

57:Foreach line in tdmodel.log

58:              If line.indexof(“LoadTime”)!=null then

59:              If LoadTime>2000 or LoadTime < 500 then

60:                CIDReport.add(LoadTime-CID(line))

61:              End If

62:            End If

63:          End For

64:        End For //TDModel元素遍历结束

65:End For //RDModel元素遍历结束

66:      return CIDReport

67:End

CID算法的主要输入有:录制时收集到的参考设备运行状态信息RDModel集合, RDModel集合中包括所有Android UI树的log(日志), screenshot(截图), tree(Android UI树)等信息.重放时, 收集到的测试设备运行状态信息TDModel, 其数据结构与RDModel相同.算法的输出是兼容性问题检测报告CIDReport, 算法具体执行过程如下.

●    算法初始化:首先将CIDReport赋值为空(第2行).

●    异常检测:算法将首先进行异常检测, 通过逐行读取测试设备的日志来检测重放期间是否发生异常, 如果检测到异常则在CIDReport集合中插入一条异常检测失败信息(第3行~第8行).

●    Android UI树检测:异常识别完成后, CID算法将需要检查的属性集合赋值给prop(第10行~第12行), 接着遍历RDModel和TDModel中所有截图、Android UI树以及日志信息并取出参考设备每棵UI树中的每一个结点n1i, 去匹配测试设备每棵UI树中的每一个结点n2i.首先判断每个根结点的子结点是否命名相同(第13行~第19行).如果相同, 进一步抽取每个子结点的prop集合属性, 对比这些属性的值是否相等:如果每个结点所匹配属性数量matchPropprop集合的属性数量相等, 则继续进入Structure check检测阶段; 如果不相等, 算法将检查n1in1i的scrollable属性是否为真, 如果为真, 说明当前控件加载不完整, 未匹配控件可能会通过Scroll操作后匹配成功, 无法确认是一个兼容性问题, 需要进一步检测.如果两个结点的scrollable属性都为假, 则在CIDReport集合中插入一条Android UI树兼容性检测失败信息(第20行~第35行).

●    结构检测:StructureCheck函数负责检测测试设备和参考设备用户界面元素的布局是否正确(第36行), 算法3描述了具体的检测过程.

算法3. 结构检测算法SC(structure check).

输入:不同UI Tree的两个结点node1, node2.

输出:兼容性问题检测报告CIDReport.

01:Begin

02:  If node1.parent=node2.parent then

03:        StgetSiblingNodeSet(node1)

04:        SrgetSiblingNodeSet(node2)

05:Foreach sibling Noden1i in St

06:         AtgetAlignInfo(n1i, node1)

07:End For

08: Foreach sibling Noden2i in Sr

09:              ArgetAlignInfo(n1i, node2)

10:End For

11:         Foreach element a1i in At

12:Foreach element a2i in Ar

13:                  If a1i not equal a2i then

14:                  CIDReport.add(StructCheck-CID(a1i, a2i))

15:                  return false

16:                End If

17:            End For

18:         End For

19:         TgetRelativePosition(node1, node1.parent)

20:         RgetRelativePosition(node2, node2.parent)

21:If [T.Origin-R.Origin] < s and [T.End-R.End] < s and [T.Origin-R.Origin] < s and [T.End-R.End] < s then

22:          return true

23:Else

24:          CIDReport.add(StructCheck-CID(T, R)

25:return false

26:            End If

27:      End If

28:End

SC算法的主要输入有:不同UI树的两个结点node1, node2.算法的输出是兼容性问题检测报告CIDReport.其中, 算法的第1行~第10行负责计算每个结点与父结点和兄弟结点的位置关系并生成结构布局图.第11行~第18行检测测试设备和参考设备相对应结点的布局关系是否一致.第19行~第28行检测测试设备和参考设备相对应结点在父结点内的相对位置是否一致.具体执行过程如下.

1) 结构布局图生成

SC算法将需要进行检测的两个结点的父结点进行对比, 如果父结点相同, 则调用API getSiblingNodeSet(∙)分别将两个结点的所有兄弟结点加入集合St和Sr(第1行~第4行); 接着, 通过使用getAlignInfo(∙)计算两个结点与其他兄弟结点的位置关系, 并将位置关系分别存储在集合AtAr中(第6行~第10行)并转换为结构布局图. AppCheck定义两个子结点的位置关系为{letf-top, top, right-top, letf-bottom, bottom, right-bottom, overlapping, contain}这8种.转换过程如下图 6所示, 左侧AnkiDroid截图的父结点是一个linearlayout布局控件, 包含3个文本框控件, SC算法会将左侧截图界面布局转换为右侧的结构布局图.

Fig. 6 Structure Check example 图 6 Structure Check示例

2) 布局关系一致性检测

接着对比集合AtAr结构布局图的位置关系是否相同:如果不同, 则说明存在兼容性问题Structure Check失败, 算法将在CIDReport集合中插入一条Structure Check检测失败信息; 如相同, 则进入相对位置一致性检测(第11行~第18行).

3) 相对位置一致性检测

这一阶段将使用getRelativePosition(∙) API计算两个结点在父结点控件内的相对位置, 其中, TR代表测试设备和参考设备的界面元素结点, Origin和End分别代表界面元素结点的起始坐标范围.如果两个结点在父结点内相对位置范围的差小于阈值σ, 则认为输入两个结点通过了Structure check; 反之, 则在CIDReport集合中插入一条Structure Check检测失败信息(第19行~第28行).

●   OCR检测

为了检测界面控件布局一致但文本显示不一致的情况, 通过Structure Check检测后, 算法将进入OCR (optical character recognition, 光学字符识别)检测阶段.OCR检测阶段通过使用OcrCheck(∙) API识别出测试设备和参考设备界面元素实际显示的文本, 并对比测试设备和参考设备实际显示的文本:如果相同, 则通过OCR检测进入Color Histogram检测阶段; 如果不同, 则将在CIDReport集合中插入一条OCR检测失败信息, 并使用ImageCompare(∙) API进一步检测兼容性问题原因(第38行~第42行).

●     Color Histogram检测

进入Color Histogram检测阶段后, AppCheck将使用ColorHistogramCheck(∙) API来检测测试设备和参考设备用户界面实际显示的颜色是否相同:如果检测结果小于阈值σ, 则可认为检测设备和参考设备的用户界面颜色一致并通过Color Histogram检测; 如不同, 则使用ImageCompare(∙) API进一步检测兼容性问题原因并在CIDReport集合中插入一条Color Histogram检测失败信息(第43行~第46行).例如, 在测试应用AnkiDroid v2.5alpha64时(如图 7所示), AppCheck首先将从参考设备和测试设备中获得的截图和Dump文件分别转换为Android UI树和结构布局图(结构布局图转换方法见算法3), 接紧着进行Android UI树检测, 当检测通过后继续完成布局检测、OCR检测以及颜色直方图检测, 如有任何检测失败, 则使用ImageCompare(∙) API进一步检测兼容性问题原因(第47行).在本示例中, 三星S7(Android 7.0)文本可以正常对齐(如图 7(a)所示), 而在红米1 (Android 4.3)中存在文本不对齐的兼容性问题(如图 7(b)所示).在布局检测阶段中会被发现, AppCheck将通过图片比较标出兼容性问题的具体位置, 以供测试人员进一步分析兼容性问题原因.通过OCR以及Color Histogram检测后, 算法将认为此次重放通过了兼容性检查(第43行~第51行), 继续进入性能检测.

Fig. 7 AnkiDroid compatibility problem detection example 图 7 AnkiDroid兼容性问题检测例子

●    性能检测

通过OCR以及Color Histogram检测后, 算法将进入性能检测阶段, 本阶段将依据定义2检测重放是否存在性能问题.如果操作的响应时间小于500ms或者大于2 000ms, 算法将在CIDReport集合中插入一条性能检测失败信息(第56行~第63行).如果操作的响应时间符合推荐标准, AppCheck将认为被测应用在测试设备上通过兼容性检测, 并返回兼容性问题检测报告CIDReport.

完成以上检测后, CID算法将分别取出RDModelTDMode集合中的下一棵Android UI树重复以上检测过程, 直到两个集合中所有Android UI树完成兼容性检测(第13行~第65行).

3 实现

我们基于上文提到的方法实现了一种基于录制/重放的Android应用众包测试工具AppCheck, 它主要包括4个模块:事件序列收集器、用户操作抽象模块、跨设备重放模块以及跨设备兼容性问题检测模块.接下来介绍每个模块的实现.

事件序列收集器的实现基于开源框架Open STF[22], AppCheck利用其中的minitouch[30]库记录各种触屏操作和Menu, Home and Back等实体键操作.同时, 为了快速提取应用界面信息, AppCheck优化了UIAutomator[32]的生成速度.用户行为的抽象模块是使用Java实现的, 它通过根据定义1中的时间自动机来解析用户操作序列.跨设备的重放模块是一个Android移动应用, 主要使用Android AccessibilityService所提供的API实现.通过使用Accessibilityservice测试设备, 无须连接adb即可完成操作序列重放.

例如, 它可调用findAccessibilityNodeInfosByID(∙) API来通过ResourceID定位UI组件, 也可以通过调用findAccessibilityNodeInfosByText(∙), 通过文本寻找UI组件, 并根据UI组件的属性值来判断搜索到UI组件是否符合要求.为了实现基于XPath定位器搜索UI组件, 该模块调用getRootInActiveWindow(∙) API获取Android UI树的根元素, 并继续调用performAction(∙) API去执行相关操作.为了防止用户在重放期间的错误操作而导致重放失败, 重放应用会自动运行一个透明窗口来防止用户的错误操作.重放应用在执行操作序列的同时也会收集测试应用各种数据(例如UI布局、应用截图), 并发送给后台服务器, 后台服务器使用Node.js实现.跨设备兼容性问题检测模块是用Java实现的, 该模块是通过对比测试设备上和参考设备收集到的数据是否一致来检测兼容性问题.并最终通过HTML5提供的画布功能, 以HTML网页形式报告检测结果.

兼容性检查中的OCR功能使用Tesseract OCR[35]引擎实现, 并使用OpenCV中的Color Histogram算法以及Yahoo的Blink-Diff[36]分别完成相似度检查以及图片差异查找功能.

4 实验设计与结果分析

为了评估我们方法的有效性和效率, 我们在8个不同的平台对AppCheck进行了测试, 包括智能手机、Phablets、平板电脑和模拟器, 对各大应用市场的100个Android应用进行了实验.在实验中, 我们主要考虑以下研究问题.

●    问题1:Appcheck是否可以支持跨设备记录/重放?如果可以, 它与现有的技术相比效率如何?

●    问题2:Appcheck跨设备操作序列收集和重放的性能开销如何?

●    问题3:Appcheck是否可以有效发现应用程序的兼容性问题?

接下来, 我们将详细介绍实验以及实验结果.

4.1 实验环境搭建

在实验中, 我们选择了一组不同的Android设备, 它们反映了Android平台上硬件和软件的多样性.所选设备的配置见表 3, 其中, 三星S5被选为测试参考设备(其运行结果作为测试断言), 其余设备被选为测试平台.屏幕尺寸范围从4.3到10, 包含所有主流移动设备:智能手机和平板电脑.后端服务器具有配置如下:8G内存, 英特尔(R)核心(TM)2四核2.67 GHz.

Table 3 Target Android smart device list 表 3 目标Android智能设备列表

为了探讨问题1, 我们选取了14个应用程序从F-Droid——一个开源的Android应用程序库和86个来自中国应用程序商店(如安智、小米等)的应用(见表 4).选定应用程序的涵盖范围广泛, 如社交网络(如微信)、新闻(如腾讯新闻)、阅读应用(如书旗小说)、流媒体应用(例如爱奇艺)、图像编辑应用(如美图秀秀)和邮箱(例如K-9邮件).为了收集实验数据进行评估, 我们还招募了4名未参与研究的学生.当他们通过浏览器与测试应用交互时, AppCheck将捕获发生的各种触摸事件.我们为每个选定应用程序收集10个以上不同的操作序列.这些操作序列将被抽象并在测试设备上重放.

Table 4 100 popular mobile applications selected from the main application markets 表 4 各大应用市场挑选出的100个流行移动应用

在这个实验中, 我们将分别使用AppCheck和Mosaic(另一个基于坐标等比例缩放以完成跨设备记录/重放的技术)来录制/重放以上100个应用.因为Mosaic没有开放源代码, 我们根据Mosaic论文中提供的方法, 基于RERAN实现了Mosaic.为了公平起见, 我们要求学生进行两次同样地交互操作, 并分别使用AppCheck和Mosaic完成操作序列的收集.为了提高重放成功率, Android设备从AppCheck服务器端下载屏幕尺寸相近设备生成的测试脚本, 并且人工审核, 以保证重放环境与录制环境相同, 审核内容包括关闭除测试应用以外的程序、网络带宽、测试脚本内容是否与测试用例相同、GPS定位位置等.

为了回答研究问题2, 我们根据实验发现, 系统开销主要在两个方面:首先, 是由提取App UI元素布局信息引起的; 其次, 是由于截图和网络通信造成的.为了评价AppCheck的系统开销, 我们选择了5个流行的应用, 并收集每个应用程序的5个操作序列.在红米Note 4记录每个操作序列的平均执行时间, 每一个操作序列测试3次选取平均值.

为了回答研究问题3, 我们使用AppCheck测试8个已知的兼容性问题.针对每个兼容性问题, 设计一个可以重现兼容性问题的测试脚本, 以测试AppCheck是否可以正确检测到兼容性问题.

4.2 实验结果及分析

根据上节的实验设计, 实验1步骤设计如下.

●    步骤1:根据每个应用的主要功能设计测试用例.原则上, 设计的用例应覆盖该应用与其他应用或系统交互的场景;

●    步骤2:将收集的事件序列转换为可跨设备的中间脚本语言脚本;

●    步骤3:在目标设备的完成重放.

重复步骤1到步骤3, 直到所有100个应用都测试完毕.

通过以上实验步骤, 实验结果见表 5表 6.

Table 5 AppCheck successfully recorded/replay applications 表 5 AppCheck成功录制/重放的应用列表

Table 6 Mosaic successfully recorded/replay applications 表 6 Mosaic成功录制/重放的应用列表

实验结果表明, 基于实验收集到的用户交互事件序列, AppCheck可以成功录制重放各大应用市场挑选出的100个主流移动应用中的87个应用; 而Mosaic在大多数场景下录制/重放失败, 只能在25个成比例缩放的场景下重放成功.这里, 成功录制重放是指收集到的用户交互事件序列转换为平台无关测试脚本后, 可以在表 3的测试设备上正确执行, 没有异常抛出.该实验数据集分为4部分, 访问地址如下.

  https://gitee.com/TETTT/appcheck_test_data_set_part_i;

  https://gitee.com/TETTT/appcheck_test_data_set_part_ii;

  https://gitee.com/TETTT/appcheck_test_data_set_part_iii;

  https://gitee.com/TETTT/appcheck_test_data_set_part_vi.

经过分析AppCheck重放失败的13个应用, 我们发现重放失败原因有以下几点.

(1)   传感器数据:AppCheck是基于GUI的测试方法, 无法获取传感器数据, 导致某些需要传感器数据的测试场景重放失败.

(2)   随机场景:某些银行类应用, 因为安全方面的考虑密码按键位置每次会随机发生改变, 导致录制/失败.

(3)   Seletor获取失败:WebView等非Android原生界面元素的应用, 无法通过辅助功能、HierarchyViewer、UIautomatorviewer和WindowManager这4种方法获得界面信息.

(4)   操作不支持:目前, AppCheck只支持Click, LongClick, Input, Swipe, MultiTouch以及EntityKey等操作的录制/重放, 某些操作, 如3个手指的MultiTouch、精确度高的拖拽操作无法支持.

(5)   不确定性:应用本身有不确定性(例如从网络上加载内容、非确定性函数的调用等)导致界面不一致.

这些不足可以通过插桩捕获传感器输入、录制过程中收集非确定性函数调用返回值等方法解决.我们计划在将来的工作中解决这些问题.

为了回答研究问题2, 表 7表 8分别展示了AppCheck在跨设备录制/重放时的额外系统开销以及AppCheck所支持操作的平均执行时间.

Table 7 Performance overhead of the 5 popular applications 表 7 五大流行应用平均性能开销

Table 8 Average execution time of the operations supported by AppCheck 表 8 AppCheck所支持操作的平均执行时间

通过分析表 7可知, 相对于被测应用的日常使用场景, AppCheck额外增加了系统开销20%左右.这是因为为了保证录制脚本的跨设备重放, 需要有额外网络通信和截图开销导致的.表 8是通过本文定义2计算得出的每个操作的平均执行时间, 通过分析表 8的实验数据可知, Click, LongClick, Input, Swipe, MultiTouch以及EntityKey等操作的执行时间都低于2 000ms, 符合Google官方给定的执行时间标准.

为了回答研究问题3, 我们选取了8个已知的兼容性问题, 其中包括6个功能相关的兼容性Bug以及2个性能相关的兼容性Bug.针对每个兼容性问题, 设计一个可以重现Bug的测试脚本以及相对应的测试环境, 以测试AppCheck是否可以正确检测到问题, 实验结果见表 9.

Table 9 Detection of compatibility problems 表 9 兼容性问题检测

通过分析表 9中的结果可知, AppCheck可以有效检测出8个兼容性问题中的6个.经过分析AppCheck未检测出的2个兼容性问题, 我们发现检测失败的原因有以下几点.

(1)   图片识别算法的局限性:当前图片识别算法在分析分辨率不同的截图时存在局限性, 导致兼容性问题误报.

(2)   应用后台修复:当软件公司收到用户反馈后, 会推送修复补丁, 应用会在用户无法察觉的情况下后台修复, 导致兼容性问题检测失败.

(3)   传感器数据:AppCheck是基于GUI的测试方法, 无法获取传感器数据, 这导致无法检测与传感器相关的兼容性问题.

(4)   不确定性:应用本身有不确定性(例如从网络上加载内容、非确定性函数的调用等)导致界面不一致.

针对以上问题, 我们计划将在今后工作中改进我们的检测算法.

5 局限性

为了克服当前众包测试人工操作会产生诸如输入错误、误操作等问题, AppCheck引入了录制/重放技术, 以确保重放可以正确完成; 同时, AppCheck使用了Android平台的辅助功能接口去完成重放操作.使用辅助功能不仅具有无需root设备的优点, 还不需要同后台服务器建立adb连接(现有的测试框架都需要使用USB接口建立adb连接, 以完成自动化测试).然而, 辅助功能MultiTouch重放功能只能在最新的版本中可使用(例如, MultiTouch的重放仅在Android API级别24以上可用).我们正在研究, 以解决目前方法的局限性.

AppCheck另外一个局限性是无法检测到某些Hybrid App的WebView界面元素, 这是因为UIAutomator无法识别WebView中的控件元素.但这个问题可以通过引入Web代理解决, 具体方法可参考我们的另一个工作X-Check[37].目前, Hybrid App正慢慢成为主流, 我们将在未来的工作中扩展AppCheck, 以支持Hybrid App界面元素识别.

目前, AppCheck主要关注跨Android设备完成触屏操作的录制/重放.同时, 因为AppCheck忽略传感器的输入数据(例如网络信息、GPS)和一些非确定性函数(例如Random(∙), Date(∙)), 会导致误报一些兼容性问题.但这些不足可以通过插桩、捕获传感器输入和录制过程中收集非确定性函数调用返回值解决.我们计划在将来的工作中解决这些问题.

6 相关工作 6.1 Android自动化测试

当前, 工业界和学术界提出了众多方法以支持智能手机测试用例的录制/重放.例如, AndroidSDK工具包中的Monkey工具[38]可以向应用程序发送随机事件流, 但这限制了Bug检测的效率.测试框架如Robotium[9]和Android Espresso[8]支持脚本生成和事件序列执行, 但是需要手工编写测试脚本.

在Android应用自动化测试领域, 基于GUI和模型以自动生成测试用例的方法非常流行.AndroidRipper[39]通过用户界面接口, 使用深度优先搜索以生成测试用例.A3E[40]则包含了两种搜索策略:深度优先和目标优先. SwiftHand[16]通过动态建立GUI的有限状态机模型, 寻求以最少事件数达到最高的测试覆盖率.ORBIT[41]基于动态GUI抓取和静态代码分析方法, 以避免生成不相关的UI事件.然而最近实证研究表明[7], 所有现有技术的覆盖率都很低(小于50%).

6.2 AndroidRecord/Replay框架

目前, 工业界和学术界已有多种针对Android应用交互行为录制/重放的框架.工业界相关研究如TestIn[18], 以在不同型号的真机上进行测试为主, 通过将应用运行在不同分辨率、不同尺寸和其他硬件配置的真机上完成对应用适配性的测试.在学术界, 以RERAN[12]为代表的记录底层日志方法, 通过记录Android底层接口得到系统操作日志流, 以完成测试用例的重放, 其精确度可以达到毫秒级.但单纯记录操作日志流方法只能在录制的智能设备上重放, 无法实现跨设备重放.Mosaic[13]是RERAN[12]工作的进一步扩展, 通过将移动设备的指令集进行抽象化处理, 将设备事件抽象为Press、Move、Release等有限的几个动作, 来描述用户的所有行为, 以达到跨设备重放用户行为的目的, 其系统开销仅为0.2%, 并能跨设备和平台重放GooglePlay中的45个应用.但Mosaic存在两方面的局限性:(1)在指令集抽象方面, 将移动设备复杂的指令集抽象为简单几个动作, 以这种方式生成测试用例不具备可读性, 无法重用; (2)在跨设备自适应方面, 由于Mosaic采用成比例缩放方式实现不同屏幕尺寸和分辨率的适配, 当应用界面布局随着屏幕旋转或屏幕尺寸、分辨率发生变化后, 该方法会失效.

以VALERA[42]为代表的插桩方法虽然可以在多个智能设备上重放, 但这种方法只能测试单一应用.当前, 多个应用交互的场景越来越普及, 单纯针对单个应用测试已经无法满足自动化测试需求.同时, 对字节码插桩会造成系统额外开销, 不能准确地评估应用运行情况; 另外, 对应用重签名会破坏应用自身的安全机制, 如对支付宝、在线商城等应用程序重签名, 会对应用程序自身的安全机制造成破坏.

SPAG[43]是基于录制/重放技术的Android应用测试工具, 它通过事件批处理和智能等待等功能以减少重放过程中的不确定性, 并整合Sikuli IDE以在每个GUI操作后通过断言(截图图片)验证应用是否正确执行. SPAG-C[44]是SPAG工作的进一步扩展, 其目的是增加测试断言的可重用性而不影响测试精度.它从外部摄像机捕获程序截图, 以进一步减少记录测试用例所需时间, 并提高测试断言的可重用性而不影响准确性.

与以上录制重放框架不同:AppCheck在录制阶段支持众包方式收集用户交互事件序列, 并转换成平台无关的测试脚本; 在重放阶段, 借助于Android辅助功能服务支持直接在众包用户的设备上进行执行测试用例.

6.3 兼容性检测

当前, 针对Android生态系统的碎片化问题已有一些工作进行研究.Han等人研究了HTC和摩托罗拉在Android问题跟踪系统中的错误报告, 指出, Android生态系统是零散、缺乏可移植性的[45].为了更好地理解Android应用程序中碎片诱导的兼容性问题, 文献[33]对191个开源软件的兼容性问题进行了实证研究, 总结了一些常见模式来检测Android应用程序中的兼容性问题, 并设计实现了FicFinder.Erfani等人提出了CHECKCAMP[46]自动化测试技术针对iOS和Android平台的原生应用, CHECKCAMP通过抽象每个平台的执行序列, 通过比较不同平台的执行序列来检测兼容性问题.X-Checker[47]是一种跨平台的应用程序开发框架(例如Xamarin), 它开发了一种差异测试技术, 以识别这些框架在源平台和目标平台API的不一致性.

与以上兼容性检测方法相比, AppCheck通过收集测试用例在不同设备上执行时生成的各种运行时相关数据(例如截图和布局信息)以检测兼容性问题, 支持检测Android应用在不同设备上运行时产生的行为、布局以及性能等方面的一致性问题.

7 结论

本文提出了一种基于录制/重放的Android应用众包测试方法, 该方法的一个重要特点是, 可以通过互联网以众包测试方式支持测试用例的生成和执行.基于该方法, 我们实现了一种基于录制/重放的Android应用众包测试工具AppCheck.通过我们的初步实验结果表明, AppCheck可以有效实现众包测试环境下Android应用用户交互行为的跨设备录制/重放, 并在重放完成后识别兼容性问题, 改进了当前方法的不足.

致谢 在此感谢中科院软件研究所软件工程技术研究开发中心的老师们以及北京城市学院2016级徐自强、李林青、汪伟光同学, 2017级吕田田、潘泽、吕冉、徐舒宁、何祎昕、李月华同学, 2018级羡喻杰、梁倩、王兆钧同学, 2019级袁迈、曹艳琦、赵一萱、夏凡、侯钰坤同学对本文实验的大力协助.

参考文献
[1]
[2]
[3]
[4]
Choi W, Necula G, Sen K. Guided GUI testing of Android apps with minimal restart and approximate learning. In:Proc.of the OOPSLA., 2013, 623-640. http://www.wanfangdata.com.cn/details/detail.do?_type=perio&id=113da7d925671a2a3ddc8afa4c68a657
[5]
Machiry A, Tahiliani R, Naik M. Dynodroid:An input generation system for Android apps. In:Proc.of the FSE., 2013. https://www.researchgate.net/publication/262392393_Dynodroid_An_input_generation_system_for_Android_apps
[6]
Mahmood R, Mirzaei N, Malek S. EvoDroid:Segmented evolutionary testing of Android apps. In:Proc.of the FSE., 2014. https://cs.nju.edu.cn/changxu/2_seminar/papers/Paper_18.pdf
[7]
Choudhary SR, Gorla A, Orso A. Automated test input generation for Android:Are we there yet?In:Proc. of the ASE., 2015, 429-440. http://winglam2.web.engr.illinois.edu/publications/2016/fse16industry-wechat.pdf
[8]
[9]
[10]
[11]
[12]
Gomez L, Neamtiu I, Azim T, Millstein T. RERAN:Timing-and touch-sensitive record and replay for Android. In:Proc.of the ICSE., 2013. http://dl.acm.org/citation.cfm?id=2486799
[13]
Zhu MHY, et al. Mosaic:Cross-platform user-interaction record and replay for the fragmented Android ecosystem. In:Proc.of the ISPASS., 2015. https://www.cs.rochester.edu/horizon/pubs/ispass15-slide.pdf
[14]
Chen JC, Xue YZ, Zhao C. Approach for GUI testing based on event handler function. Ruan Jian Xue Bao/Journal of Software, 2013, 24(12): 2830-2842(in Chinese with English abstract). http://www.jos.org.cn/jos/ch/reader/view_abstract.aspx?flag=1&file_no=4399&journal_id=jos [doi:10.3724/SP.J.1001.2013.04399]
[15]
Amalfitano D, Fasolino AR, Tramontana P, De Carmine S, Memon AM. Using GUI ripping for automated testing of Android applications. In:Proc.of the ASE., 2012. http://www.wanfangdata.com.cn/details/detail.do?_type=perio&id=CC0213271803
[16]
Choi W, Necula G, Sen K. Guided GUI testing of Android Apps with minimal restart and approximate learning. In:Proc.of the OOPSLA., 2013. http://www.wanfangdata.com.cn/details/detail.do?_type=perio&id=113da7d925671a2a3ddc8afa4c68a657
[17]
[18]
[19]
[20]
[21]
Wang JJ, et al. Towards effectively test report classification to assist crowdsourced testing. In:Proc.of the ASE., 2016.
[22]
Smartphone test farm (STF).2018.https://openstf.io/
[23]
[24]
[25]
[26]
[27]
Wei L, Liu Y, et al. Taming Android fragmentation:Characterizing and detecting compatibility issues for Android apps. In:Proc.of the ASE., 2016. https://dl.acm.org/doi/10.1145/2970276.2970312
[28]
Choudhary SR, Prasad MR, Orso A. X-Pert:Accurate identification of cross-browser issues in Web applications. In:Proc.of the ICSE., 2013. http://dl.acm.org/doi/10.5555/2486788.2486881
[29]
Fazzini M, et al. Automated cross-platform inconsistency detection for mobile apps. In:Proc.of the ASE., 2017. https://www.cc.gatech.edu/grads/m/mfazzini/publications/2017_ase_fazzini.pdf
[30]
[31]
[32]
[33]
Kang Y, et al. DiagDroid:Android performance diagnosis via anatomizing asynchronous executions. In:Proc.of the SIGSOFT., 2016.
[34]
Kang Y, Zhou YF, Gao M, Sun YX, Lyu MR. Experience report:Detecting poor-responsive UI in Android applications. In:Proc.of the 2016 IEEE 27th Int'l Symp.on Software Reliability Engineering., 2016.
[35]
[36]
[37]
Wu G, He M, et al. Detect cross-browser issues for JavaScript-based Web applications based on record/replay. In:Proc.of the ICSME., 2016. http://www.wanfangdata.com.cn/details/detail.do?_type=perio&id=9061706
[38]
[39]
Amalfitano D, Carmine SD, Memon A, et al. Using GUI ripping for automated testing of Android applications. In:Proc.of the ASE., 2012. https://www.researchgate.net/publication/254463886_Using_GUI_ripping_for_automated_testing_of_Android_applications
[40]
Azim T, Neamtiu I. Targeted and depth-first exploration for systematic testing of Android apps. In:Proc.of the OOPSLA., 2013. http://www.wanfangdata.com.cn/details/detail.do?_type=perio&id=62f4f77c5164b2f607a325a59cae2ed4
[41]
Yang W, Prasad MR, Xie T. A grey-box approach for automated GUI model generation of mobile applications. In:Proc.of the FASE., 2013. http://taoxie.cs.illinois.edu/publications/fase13-mobile.pdf
[42]
Hu Y, Neamtiu I. Valera:An effective and efficient record-and-replay tool for Android. In:Proc.of the Int'l Workshop on Mobile Software Engineering and Systems.ACM, 2016, 285-286. http://www.cs.ucr.edu/~yhu009/papers/mobilesoft16.pdf
[43]
Lin YD, Chu ETH, et al. Improving accuracy of automated GUI testing for embedded systems. In:Proc.of the IEEE Software., 2014. http://www.wanfangdata.com.cn/details/detail.do?_type=perio&id=5a72964efc69d9c2b59d4009a3e2f086
[44]
Lin YD, Rojas JF, et al. On the accuracy, efficiency, and reusability of automated test oracles for Android devices. In:Proc.of the IEEE TSE, Vol.10., 2014. http://www.wanfangdata.com.cn/details/detail.do?_type=perio&id=d4d97b29ea983583dd1c5550378f980c
[45]
Han D, Zhang C, Fan X, Hindle A, Wong K, Stroulia E. Understanding Android fragmentation with topic analysis of vendor-specific bugs. In:Proc.of the WCRE., 2012. http://www.wanfangdata.com.cn/details/detail.do?_type=perio&id=CC0212986258
[46]
Joorabchi ME, Li M. Mebah A.Detect inconsistencies in multi-platform mobile apps. In:Proc.of the ISSRE., 2015. http://salt.ece.ubc.ca/publications/docs/issre15.pdf
[47]
Boushehrinejadmoradi N, et al. Testing cross-platform mobile app development framework. In:Proc.of the ASE., 2015. https://ieeexplore.ieee.org/document/7372032
[14]
陈军成, 薛云志, 赵琛. 一种基于事件处理函数的GUI测试方法. 软件学报, 2013, 24(12): 2830-2842. http://www.jos.org.cn/jos/ch/reader/view_abstract.aspx?flag=1&file_no=4399&journal_id=jos [doi:10.3724/SP.J.1001.2013.04399]