![TARS 系统 —— UI 自动化解决方案](https://static001.infoq.cn/resource/image/04/39/046c06e402783fcf0yy9b34efeea6339.jpg)
1. 立项背景
业务痛点
互联网 APP 测试在“快速迭代”的开发模式中进行的,每次迭代都会伴随着各种各样的测试,其中以功能集成测试为重要和主要的一个方面,又因版本迭代过程中需要进行的功能特性验证和已有功能回归,要求测试人员既能测试性功能的各种特性,又要保证原有功能不受影响。在频繁的迭代工程中,APP 的功能和结构越来越复杂,新代码的影响越来越广,回归范围越来越多而且 APP 端有各种机型和系统版本,这就造成了潜在的漏测风险和越来越多的重复劳动。以去哪儿网机票业务为例,冒烟 Case 测试点就有 300+,月版回归 Case 测试点有 1000+,每次月版回归相当耗费测试人员精力,测试的质量依赖于测试人员的质量,并且还有可能遗漏,每次月版回归都要至少 10 个 QA 集中测试一天,效率十分低下,并且还经常出现线上 bug 和故障。
为了解决其中工作量最大的每次都要进行的回归测试工作量问题,提升工作效率,降低潜在的遗漏风险,减少重复的工作,该项目希望能够通过一种所见即所得的方式完成脚本的录制,即使测试人员不会编程不懂脚本,也可以通过正常用户的点击拖拽等操作,自动完成脚本的录制,从而大幅度降低企业和项目的自动化维护成本。针对移动厂商跨多端的特点,需要开发一套 UI 自动化测试解决方案。支持 TestCase 的自动重复执行,在多种机型兼容性测试,后端响应数据可控,并对执行结果的请求数据、响应数据以及界面截图进行验证,断言执行结果的正确性。
2. 方案选型
2.1 测试分类
自动化测试是随软件开发技术一并发展的一种测试技术。软件测试包括白盒测试与黑盒测试,而自动化测试最早使用白盒测试中的单元测试。这种测试方式较为高效,易于实现。黑盒自动化测试兴起稍晚,但目前应用也非常广泛。黑盒自动化测试原理是用程序和脚本模拟用户的操作行为,通过智能方式验证软件的关键检查点。在桌面软件和 Web 技术兴盛时代,涌现出大量优秀的自动化测试工具如 QTP 和 webdriver,这些工具经过不断完善已经逐步成熟,能够较好地满足传统应用的自动化测试需求。
APP 的自动化测试包括以下这些:
1、按测试目的划分:
功能测试:对产品和模块的各个功能进行测试。
性能测试:对系统的各项性能指标进行测试。
压力测试:测试软件或系统的负载能力,挖掘隐患。
兼容性测试:对产品和软硬件之间的兼容性进行测试,比如软件在各种不同安卓机型上的兼容性。
安全性测试:通过不同方法发现软件的安全性问题,比如信息泄露、非法使用、恶意破坏等等。
其他专项测试:比如弱网络测试、耗电量测试、流畅度测试等等。
2、如果根据软件开发阶段来划分,每个阶段又可以做:
单元测试:对程序中的独立模块进行白盒测试,目的是检验软件基本组成单位的正确性。
集成测试:通过对单元模块进行组合测试,目的是验证单元模块之间的接口是否正确。
系统测试:对整个系统进行完整测试,验证整个系统的正确性与合规性。
回归测试:当软件发生变更的时候,对这次变更可能受影响的功能模块进行验证。
验收测试:测试的最后一个阶段,软件发布或者上线前确保软件质量。
3、其他常用测试概念:
冒烟测试:冒烟测试是对软件最基本的功能进行简单测试,低成本的判断软件是否可测。
冒烟测试来源于硬件的测试,当电路板做好后,首先会进行一次加电,如果没有冒烟才会开始进行接下来的测试,否则说明产品没有达到最基本的质量要求,需要重新制作。
探索性测试:探索性更多的依赖测试人员的个人经验或者特长,依靠的是测试人员的主观能动性。
探索性测试的重要性可以参考游戏测试领域,千千万万的玩家会在各种意想不到的环境下以意想不到的方式来进行游戏,所以游戏的测试者不仅要掌握系统的测试方法论、先进的测试工具以外,还要有丰富的游戏经验和探索的测试思维。
功能集成测试、性能、兼容性、界面交互、访问权限、用户使用体验等等,其中琳琅满目的各种型号、版本的设备数不胜数。其中功能集成测试,在每次版本迭代过程中需要进行的功能特性验证和已有功能回归,要求测试人员既能测试性新功能的各种特性,又要保证原有功能不受影响。
随着时间的推移,APP 的功能越来越完备,结构越来越复杂,每次代码改动的影响范围越来越广,而且有些测试条件构造原本就很复杂,相应的回归范围也会随着越来越多,加上频繁的迭代,这就使质量保证变为过多的重复性有意义的劳动,在执行过程中也会一定程度上造成潜在的漏测风险。受限于经济、人力等因素,想要在一定的时间内完全兼容性测试也十分困难。
综上所述:一是功能测试中的变化可能不好执行,二是回归频繁重复,三是兼容性会成倍加剧以上两种情况,所以为了解决这些问题,提升工作效率,针对 APP 特点,需要开发一套 APP 自动化解决方案。支持打包之后自动执行测试用例,在多种机型兼容性测试,降低测试人员的重复劳动,提升测试质量,并可对执行结果的判断是否通过的自动化测试系统。
2.2 UI 自动化发展趋势
智能手机的应用带来软件测试技术新的革命,相比于传统测试,手机测试在硬件上脱离了 PC 的传统架构,和用户的交互方式存在很大区别。手机软件在使用上具有独特的操作方式,比如用户的滑动、触摸和点击等操作,如何实现手机客户端上的自动化脚本执行成为这类测试的难点。又由于 IOS,Android 两大平台的市场格局,大部分手机应用都需要支持多个平台,它们的基础架构不尽相同,使得手机自动化测试框架对兼容性有了更高要求。移动设备的智能化及普及化,也使越来越多的人享受着科技创新和发展所带来的便捷生活。尤其是今年以来,全世界的网络流量第一次历史性的移动端超过 PC 端,这说明未来的趋势是移动市场将占据主导地位,移动设备上应用最广泛的当属 APP,因其操作便捷性,友好的交互,以及切实的解决用户的问题,首当其冲的为人们所广泛使用。要做好手机客户端的自动化测试,首先要解决测试脚本的健壮性和平台的兼容性问题。
在 APP 的软件使用过程中,需要尽可能的减少用户使用过程中遇到的各种问题,避免因 APP 出现的 bug 给用户带来使用上的不便,使原本带来的便捷,变得不那么“便捷”。所以在开发过程中,就需要把质量作为重中之重,进行全面深入的质量测试。而互联网行业瞬息万变,要满足日新月异的变化在业务和技术上持续不断的推陈出新。因此 APP 的开发模式采用了软件工程中的“快速迭代”模式,以应对快速变化带来的各种影响,既要保证全面的测试覆盖,又要用更小的时间成本就成了重点。
近几年手机客户端自动化工具发展较快,出现了不少自动化框架,比如基于 Android 平台的 UiAutomator 和 Robotium ,IOS 自带的 Instrument 等,但是这些工具都没有有效解决平台兼容性问题,无法用一个统一的框架驱动。
不同平台测试,而且在识别客户端内容和元素时也存在偏差,测试脚本不够稳定健壮。目前具有良好兼容性且框架成熟稳定的工具是 Appium。Appium 是一个开源的、跨多平台多语言的测试框架,相比于其它框架,它编写测试脚本和运行测试时不需要对源码重新编译,在脚本的编写和实现上也对编程语言没有太多要求,测试更轻量灵活。此外,Appium 采用了 C/S 架构 ,提供了一个统一的对外服务接口,使得客户端或模拟器的交互和控制透明化。
目前市面上已有一些 APP 的测试框架,有的跨端能力不强,支持 www 网页测试,有的只支持 Android、IOS 移动端,同时支持 Android、IOS、www、小程序的少之又少,有的解决的功能测试的问题,但是基于快速变化需要维护的成本远大于使用的成本,都没有很好的解决痛点。
![](https://static001.infoq.cn/resource/image/b4/e9/b426fed442b5dc152620fecefffae9e9.png)
![](https://static001.infoq.cn/resource/image/90/33/9077b5e8c927e59eb45yy42db8c7d033.png)
TARS 系统通过自研方案,使用人工智能图像识别技术,OCR 文字识别技术,以及通用 UI 元素查找引擎,可以做到跨端,跨平台。并且基于 C/S 框架,通过发 http 请求的方式操控移动设备,可以做到多语言支持。结合 Jenkins 等持续集成工具,搭建一套自研 UI 自动化测试系统没有任何问题。
TARS 名称来源于《星际穿越》这部著名的科幻电影,TARS 是电影中功能强大的智能机器人,辅助主角完成了很多艰巨的任务。
![](https://static001.infoq.cn/resource/image/87/89/8768d87cc88dcbc943a2c3c12aee7289.jpg)
3. 框架搭建
3.1 功能梳理
![](https://static001.infoq.cn/resource/image/83/6e/830737b59ce3682e58e31a0df3310e6e.png)
3.2 框架结构
采用 TARS+Jenkins+STF 作为整体控制调度的系统,整体系统分为应用层,控制层,设备层。通过设备集群控制系统,选择设备集群,批量跑自动化测试 Case。Case 录入系统可根据图片,UI 元素定位识别来确定 Case 的操作步骤。几乎所有系统采用了代码量较少、对数据控制更灵活 python 作为编程语言。
首先是准备:控制系统通过设备管理平台选择好需要进行测试的设备,安装好客户端,配置好测试环境参数,如 AB,MocK。控制系统通修改响应数据,保存响应数据到某个 testcase 中(以备下次 MocK),直接返回服务器响应,还是获取对应 testcase 的 Mock 数据,可控制 APP 端获得的各种数据字段。
然后开始执行 testcase:在指定设备中调起客户端,执行指定 testcase。
最后是 diff 断言阶段,对比返回结果,请求参数,样式图片之间的差异。
控制系统,正确的控制调度每一步所要执行何种动作。能够正确转发请求并截获响应。
Mock 系统能够按照配置正确或把后端响应数据修改、保存再返给前端,或把 Mock 数据返给前端,并正确保存请求和响应的各个数据,完成 http 转发代理。
设备平台,能根据现有挂载到平台上的设备,判断可用情况,提供设备共测试使用。
Case 录入,可让用户手工操作截图,UI 元素识别,OCR 文字识别等方式录入 Case,简化 Case 录入方法。
图片和数据 diff 系统,把测试数据和截图准确 diff,排除可能的影响因素(如数据使用 Mock 接口)。
![](https://static001.infoq.cn/resource/image/9a/92/9a134e971345ed341ea5346c8815c192.png)
![](https://static001.infoq.cn/resource/image/42/af/42d8d8255c2a325a75dd65359f5887af.png)
![](https://static001.infoq.cn/resource/image/8a/6a/8a4532473c178a0ae298d302aa35516a.png)
3.3 元素查找方案
自动化 Case 编写需要测试框架层提供对界面元素进行操作的功能支持,其中最重要的就是对元素的查找和操作。
编写 Case 或者录制 Case,我们需要的一个基础能力就是如何准确又稳定的获取到页面元素。它决定了我们录制 Case 的成本以及 Case 维护的成本。
如果查找不够准确,将直接导致我们的 Case 运行不稳定,影响执行结果的准确性。
如果页面结构发生变化,我们查找元素的方式不够稳定,那每次功能迭代都需要重新对 Case 进行维护,那样维护 Case 的成本远大于使用自动化测试节省下来的测试成本,自动化测试也就没有意义了。
一个好的元素查找方式,是需要我们在 UI 自动化测试实践过程中不断探索改进的。
目前市面上流行的元素查找方案主要有三大类:
1. 通过 XPath 查找
我们获取到的 UI 元素基本都是 XML 形式组织的,XPath 是一门在 XML 文档中查找信息的语言,XPath 可以用来在 XML 文档中查找元素和属性。XPath 可以精准定位一个元素,但是它要依赖 XML 文档结构的上下文,可能会受到布局改动的影响,且在 iOS 上性能不佳。
2. 通过标识查找
可以通过给元素添加标识(accessibility、testID 等)来直接定位一个元素,或者也可以对文本内容进行匹配查找元素。这样,即使我们的页面结构发上了变化,但是需要操作的元素标识没有变,那么我们就不需要对代码进行修改。但是我们需要对特定的元素添加标识,也有一定的工作量和维护成本。
3. 通过图像识别查找
相比于白盒的元素查找方式,图像识别这种无需关心页面元素结构,非常自由的元素查找方式好像是自动化测试的未来。只需要通过对想操作的元素进行截图,就可以根据图片找到该元素并进行操作。
但是目前这种方式还非常的不成熟,图像查找的准确度比较差,而且不同的机型上,很可能图片不能通用,需要针对机型重新截图。
在实际使用场景中,我们为了满足各种情况,结合使用以上三种元素定位方式。通过 POCO 库对它们进行了封装。
POCO
POCO 是一个跨平台的 UI 自动化开源框架。它底层封装了各种自动化工具,例如:WDA、UIAutomation 等。
POCO 将其他 UI 自动化工具查找元素的功能进行了统一的封装,为 Case 编写提供了方便灵活又平台一致的元素操作 api。
POCO 可以很方便的实现以下功能:
元素属性或 id 定位
元素相对位置查找
元素正则匹配
元素交互控制
POCO 获取元素的方法是使用工厂模式来实现的,所以我们也可以很方便的扩展自定义的功能,比如图像识别或 OCR 文字识别。
由于 POCO 是基于 python 实现的,所以我们可以很方便的部署使用,调试脚本也非常容易。
3.4 Case 编写设计模式——Page Object Model
仅仅有框架层提供的功能支持还远不能将 Case 维护的成本降低到一个可接受的范围。
因为我们在真正编写 Case 的时候,需要编写很多业务逻辑的代码。我们一般会遇到两个问题:
编写一个 Case 就会涉及比较多的界面操作,只编写一个 Case 就已经非常耗时了。
业务逻辑或元素发生变化,所有相关的 Case 都要改一遍,如果 Case 越来越多,维护起来将是一种灾难。
所以我们需要一种合理的设计,能够尽量重用代码,并将具体的业务逻辑进行抽象,不必写很多繁琐的元素查找操作代码。
Page Object Model (POM) 直译为“页面对象模型”。这种设计模式旨在为每个待测试的页面创建一个页面对象(class),将那些繁琐的定位操作封装到这个页面对象中,只对外提供必要的操作接口。
POM 将页面定位和业务操作分开,分离了测试对象和测试脚本,如果 UI 更改页面,测试脚本不需要更改,只需要更改页面对象中的某些代码就可以,提高了可维护性。
比如说,我们需要测试航班列表页的搜索结果,可以直接调用首页对象的功能跳转到机票首页,再调用机票首页对象的选择城市和日期搜索功能跳转到航班列表页,然后在航班列表页对想要测试的数据进行断言。
![](https://static001.infoq.cn/resource/image/4e/2f/4e994b0f48b66f8e0016dbc1e672592f.jpg)
![](https://static001.infoq.cn/resource/image/25/0e/251d756a40ae6c7cde1cb7f5a54e2e0e.png)
如何实现 POM 设计模式
一个 Page Object 对象,有两方面特征:
自身元素
实现功能
自身元素就是实实在在的页面元素,实现功能就是这个页面对象提供的业务功能,比如用户登录。
这样,即使页面元素和交互逻辑发生了变化,也可以只更改很少的代码就可以让相关的 Case 都能正常运行。
POM 设计模式有以下几个设计规范:
public 方法代表 Page 提供的功能
尽量不要暴露 Page 的内部细节
不要 assertion
方法可以返回其他 Page Objects
Page Objects 不用代表整个页面,可以是任意一个部分
一样的操作,不同的结果应该分开(正确登录,错误登录)
基于 POM 设计模式,我们做到了很低的 Case 维护成本。即使页面进行了大改版,Case 也不需要变动。
![](https://static001.infoq.cn/resource/image/00/79/006b23928b262f4c944ef5ff0321c579.png)
![](https://static001.infoq.cn/resource/image/58/6f/581ce34bb294ae89321d6b6519efb66f.png)
3.5 Json Mock 方案
实际运行 Case 的过程中,我们发现除了设备环境的问题,还有一个问题非常影响 Case 执行的成功率,就是 Case 需要的数据线上环境不能稳定的提供。
这个问题理论上提供一套写死的数据进行 Mock 即可,但是很多 Case 需要验证生单,这里用 Mock 的数据会失败,并且一定程度上,我们希望能够使用实际的数据进行验证,这样可以同时发现线上接口的问题。
于是我们就需要针对各种数据不稳定的问题具体问题具体分析。
问题一:如何实时获取测试需要的线上数据
由于我们业务的复杂性,数据在一定程度上是不可复用的。比如一套生单的数据,只能使用一次,下一次使用就失效了。另外,假如我们想测试往返合单的 Case,我们需要知道哪个航线有这种类型的报价。基于这个客观情况,我们的测试团队开发了一套基于线上数据的各种条件筛选的主流程参数生成接口。
该接口会对历史存储的线上数据进行筛选,根据我们指定的数据特征,返回给我们对应的线上报价。这样,我们就可以准确的拿到特定的数据进行测试了。
问题二:如何对线上数据进行特定的处理
比如线上经常会做一些活动,页面上就可能非预期的弹出一些弹窗,阻拦我们的操作。如果我们的 Case 对这些情况都做对应的处理,那将会极大的加大 Case 的复杂度,而且要判断各种情况也会降低 Case 执行速度。
还有我们测试生单可能需要不同类型的乘机人,我们要保证账号中有这样的乘机人类型。
对于这种情况,我们就需要对线上数据进行实时的修改,以保证 Case 执行需要的数据环境。
我们通过 json mock 工具来实现该功能。我们将对接口的处理写在 Case 中,开始跑 Case 的时候,Case 将处理配置发送给客户端,当客户端获取到数据以后,先被 Mock 工具拦截根据 Case 发送过来的处理配置对数据进行处理,比如删除掉活动弹窗节点,或者替换乘机人数据。处理完以后再继续将数据返回给业务代码。这样,就可以一定程度上将线上数据 Mock 成我们需要的数据,保证 Case 执行的正确率。
![](https://static001.infoq.cn/resource/image/72/d9/726cef7f4e071410bd055a5c6ff509d9.png)
3.6 持续集成方案
在实际的自动化使用场景中,我们一般是需要在日常的开发和提测过程中进行自动化测试。整个过程一般包括以下几个阶段:
构建开发分支最新 beta 包
安装到自动化设备上
设置运行环境
执行自动化 Case
执行结束发送结果报告
而且,根据不同的使用场景,我们会执行不同的测试,比如:
冒烟测试——回归重点 Case,保证提测的基本质量
业务回归测试——回归需求相关的业务 Case,辅助 QA 对历史 Case 进行回归
性能测试——对比本次修改有没有造成性能的下降
兼容性测试——测试在不同设备上的稳定性
由人来操作肯定是不现实的,所以我们需要一个持续集成的工具来帮我们自动完成这些工作。
我们使用 jenkins 作为我们持续集成的平台,整个流程大致由下图所示:
![](https://static001.infoq.cn/resource/image/75/e8/75b9c63e829d99b03bcf6b49653085e8.png)
在开发提测的时候,会自动触发自动化任务的构建:
![](https://static001.infoq.cn/resource/image/55/c4/5560488395010419fa3d6eea05e1f1c4.png)
QA 同学也在测试的时候可以选择相关的业务 Case 进行回归。
4. 实践过程
![](https://static001.infoq.cn/resource/image/98/a5/9817e608b30d70d6545c950c298ce1a5.png)
到 9 月份整体收益:
接入研发流程:109 个项目(9 月份 57 个)和执行 205 次(9 月份 90 次),自动化回归节省人力约 370 pd(9 月约 180 pd);接入发版流程:Q3 临时灰度版、临时版和月版共发版 24 次( iOS 6 次和安卓 18 次),执行 UI 自动化 100 次左右(覆盖主包和 8 个安卓渠道包),发版回归人力节约 150+ pd;DailyRun:每天执行 2 次进行线上巡检;缺陷拦截:发现 2 个崩溃和 3 个 P1 bug。
作者介绍
邹德文,去哪儿网移动应用开发总监,负责机票售前与客户端的管理工作。2012 年加入去哪儿,先后任职于攻略和机票事业部,擅长客户端和跨端技术栈 RN,Flutter,对设备指纹有深入研究,主导了机票国内主流程 RN 迁移,TARS 自动化测试系统的开发和推广。
崔宇,2018 年加入去哪儿网,主要负责机票主流程、机票 iOS 客户端、TARS UI 自动化系统、iOS 端指纹加固等方面的工作。
头图:Unsplash
来源:Qunar 技术沙龙 - 微信公众号 [ID:QunarTL]
转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
评论