这篇文章详细的记录了我们在 UI 自动化方面,由 ‘已有方案与实际需要之间的差距’ 带来的一系列思考 --> 工具的选择 --> 方案设计 --> 方案落地使用的完整过程。想向大家传递清楚我们 “为什么要做”,“为什么要这么做”,“具体怎么做”,“要作用于哪儿”,“怎么实现完整的闭环” 这几个部分。希望可以给对 UI 自动化需求/回归测试提效有需求的同学 or 团队带来一些借鉴。
《Succeeding with Agile》一书中提出了测试金字塔这个概念。它将软件测试按照不同的粒度进行分类,越上层代表测试的集成度越高但执行越缓慢;越下层代表测试的隔离性越好但执行越轻量,所以一个快速、可维护、覆盖范围合理的测试模型可以大致概括为:70% 小而快的单元测试 +20% 覆盖率高的接口测试 +10% 的 UI 测试。
然而,在大家固有的认知中,UI 自动化投入高、维护成本大、运行稳定性差,我们要怎么合理的分配业务场景和时间上的投入,才能最大程度的提升回报率呢?
对于有道来说,软件部分的测试也一直没有形成广泛而通用的 UI 自动化方案。随着业务的高速前进,客户端的发版愈加频繁,发版又通常节奏紧凑且优先级高,每次发版都需要进行 “核心 Case” 的回归以保证 APP 主流程、主功能的完好。单对 “有道精品课” 这一个 APP 来说,平均每周发版一次,每次发版有 4-5 个小组需要进行 “核心 case” 的回归,每个小组大概花费 4 小时的时间。而” 核心 case“又不常变动,所以使用 UI 自动化来解决发版回归测试导致的人力浪费问题是非常合适的。
下面我从前期的调研总结、最终方案、重点模块的原理与优化方案介绍、使用效果这几个方面为大家分享一下我们安卓端目前采用的 UI 自动化方案。
通过调研目前各互联网公司公开出来的已有方案,以及结合公司内其他同事的调研、实践文档,我将 UI 自动化包含的工作规整为 5 类,分别是:构造 Case、校验 Case、修改 Case、回放 Case、回放结果。下面将分别说明每一类中我所期望的方案与目前已有方案之间的差异。
构造 Case 是指基于 Case 的路径,模拟用户真实的手势操作。
最常用的工具是以 selenium 为代表的,通过写代码来定位目标元素,再调用工具内置的函数对定位到的元素进行操作。但真实用起来会发现这要求测试同学学习语法、写代码,上手门槛高而且编写 Case 花费时间长;后来出现了 smart Auto 这种提供了一些语法糖的工具,使用更贴合人们语言的伪代码,虽然降低了测试同学的学习和应用成本,但是依然需要经历将实际的手势动作翻译为代码再编写出来的冗余过程;后来又有了 Espresso 一类可以录制操作的工具,可是他们大多需要手机连接电脑,在电脑 “投影” 过来的屏幕上用鼠标模拟手势操作,仍然是比较耗时且无法满足我们一些特殊的情景(比如二维码扫描功能等)。
基于以上的调研和分析,我们希望能有一个工具不需要翻译成任何的代码语言、直接在手机端就可以录制操作。这样既降低了测试同学的使用门槛,又缩短了构造 Case 的时间。
校验 Case 是指用户一系列操作后,判断结果是否符合预期,也就是说是否到达了我们希望的页面,页面中的内容、排版、样式甚至是到达消耗的时间是否符合我们的预期。
而目前对 Case 结果的校验方式,大多数都是判断某个文案或者某个元素 id 是否存在。但很多情况下,文案和元素 id 的内容是动态变化的,而且仅仅通过某个文案存在 or 某个元素存在只能证明这个元素是正常的,并不能证明 UI 层面的正确性,所以我们希望可以在不写代码的基础上,做到真正的对 UI 展示进行校验。
Case 的维护成本是 UI 自动化饱受诟病的部分,原因是校验和构造 Case 都是通过硬编码的方式生成的,无论操作路径更改还是结果更改还是页面元素更改都需要对 Case 进行修改,修改又需要重复 “查找元素、写代码” 的过程。虽然我们选择了不易变更的核心 Case 做 UI 自动化,减少了一部分的维护成本,但我们仍然希望可以进一步减少对 Case 的维护。
执行阶段只要保证 Case 可以稳定、较快反应速度的回放即可,只要前期构造 Case 和校验 Case 部分精心设计,回放一般不会有太大的问题。
但对于回放结果的处理上,一方面是目前已有的工具很多都只展示当次回放的结果,或者将回放结果存储在手机端,并不能对结果长期的保存、对比。可对于发版的回归测试来说,我们需要对每次的结果以及回归内容有一个长期的记录,方便发版过程中各角色人员实时查看,更是保证数据的可回溯性;另一方面我们希望可以对结果二次评论、手动确认,保证每个执行失败的 Case/异常情况都有确定的人员进行跟进并有地方记录和保存跟进结果。
基于以上的调研和对调研结果的分析,再结合我们的预期,我们最终选用的方案是:
1、构造 Case:对开源工具 Solopi 二次开发,使其更贴合我们需要的手势/操作,达到纯手机端录制操作步骤的效果,Solopi 具体的使用和原理在这里就不做详细介绍了,大家可以移步 GitHub 查看官方文档。
2、校验 Case:对关键步骤的执行结果进行截图,使用 openCV 与已知该步骤正确的图片进行对比,达到校验 UI 层面是否正确的期望。另外直接截图对比的方式也省去了校验规则修改带来的 UICase 的修改工作。
3、回放结果:搭建测试报告平台,扩展 Solopi 的 schema,实现回放结果的持久化和二次批注功能。
用户在手机端使用 Solopi'对 “核心 Case” 的操作步骤进行录制,录制过程中在需要做校验的位置截图并对该位置的截图命名,录制结束后会生成录制脚本的 JSON 文件和一批截图,这两部分产物我们可以通过平台的 “UI 信息管理页” 进行上传录入&维护更新。通过这一步我们确保了参照系的截图是经过人工检查的,后续回放过程中产生的截图和这一版本的截图进行对比,结果才具有可靠性。如果某个步骤校验的页面结构/内容发生了变化,我们也仅需要在平台中将对应的这一张截图进行替换即可,大大节省了 UICase 的维护成本。
录制 UICase 的过程中,很多用户的操作都会对数据库产生影响,以往我们会使用 Mock 接口的方式处理 post 请求,但 Mock 接口依然需要大量的维护成本且并没有做到完全真实的模拟用户操作。所以我们采用将请求录制下来,简单的筛选后复用的方式,在回放结束后搭配清理数据的步骤,就可以保证录制场景从 UI&接口两个方面的可重复回放性。
我们将"为该 UI 操作开始前构造数据的一系列接口、该 UI 的操作信息、该 UI 操作结束后清理数据的一系列接口"按照正确的顺序拼装成一个 Case,多个 Case 再次组装,这样就获得了回放单元——执行集。
回放是以"执行集"单位进行的,测试同学想对某个执行集进行回放的时候,只需要在平台下载一个用于回放的 Zip 包到本地,将电脑和手机连接,运行 Zip 包中的 EnvController 即可开始回放。
EnvController 会先自动构造出本次回放需要的环境,然后驱动 orderController 按照执行集中所有 Case 的配置,一步步执行接口请求 or 调用 Solopi‘进行回放。当所有的步骤完成后,ReplayCore 会把回放结果收集起来通过 PicHandle 服务将本次回放的截图和参照系截图做对比,生成最终的结果。
最终结果上传到平台后,不同的结果状态会有不同的标识,对比有异常的图片会用红色框标出,方便测试同学 check。测试同学还可以对回放结果进行二次的人工审核、批注和状态的更正。也可以将本次回放失败的 Case 一键生成一个新的执行集,进行新一轮的回放。
如果本次执行步骤是接口请求,那我们通过 OrderController 直接发送 HTTP 请求,这个过程是同步的,我们只需要等待 HTTP 的响应结果即可。但回放 UI 操作是通过 adb 调用 Solopi'的 schema 来进行的,这个命令是异步的,并且回放 UI 操作所花费的时间是不固定的,那我们该怎么在 Solopi'端回放结束后通知到 OrderController 进行下一步的操作呢?
这个过程中我们经历了几次的尝试,最终采用的方案是在 OrderController 调用 adb 的同时,向手机端的 NowOrder 文件写入当前正在执行第几步,当 Solopi'端回放结束的时候通过 NowOrder 文件中的内容作为参数再通知 OrderController 进行后续的指令查找和判断,这样就保证了执行的顺序性。
这样做还有一个好处,如果回放因为各种各样的原因被暴力终止了(电脑没电了、手机和电脑断开了连接等),我们也可以通过 OrderController 的日志或者手机端 NowOrder 的内容快速判断出当前被中断的是哪一个步骤,使用人工驱动 OrderController 的方式继续回放。
我们有了参照系图片和待验证图片后,就希望通过图片对比的方式筛分图片,一开始选用了 md5 的方式对比,但由于 md5 的对比太过于严格,而页面 UI 常常有 1px 量级甚至更小的差距,但这些差距是可以接受的,并且通过 md5 对比后虽然能得到两张图片是否一致的结论,但难以标注出有差异的区域,所以为了让测试同学更加清晰的看出两张图片的区别,我后面选取了 openCV 图片对比的方式,先将截图中手机自带的工具栏裁剪掉,减少噪声后再做对比,并将对比不一致的位置使用红色方框标出,大概实现的效果如图:
试用一段时候后,我们发现,如果图片有差异的点比较集中,就会导致图片中红框密集,甚至无法看清截图原本的样子,给测试同学人工 check 带来一些不便,于是在对比图片的代码中优化了红框放置规则的算法,解决了红框相互密集覆盖的问题。
虽然实现方案中涉及到的模块比较多,但这些对于测试同学来说全部是黑盒的,测试同学只需要在平台中简单的操作和配置即可,所有的用户相关的操作如下:
为了达到无人值守的效果,我在很多位置都增加了通知功能,正常结束/各种异常的情况都能第一时间通知到执行人,保证 UI 自动化回放开始到验收结果之前,都不需要再投入人力。
在这段时间的探索和实践中,我认为 UI 自动化不应该为了有而有,也不应该是别人都有我们也要有。我们并不能通过一个团队是否有 UI 自动化或者什么其他的测试手段来评判一个测试团队是否先进和高效。UI 自动化应该是随着业务迭代、业务形态的变化趋势应运而生的,在这个基础上找到合适的业务开展 UI 自动化工作,才能得到更高的性价比。通过这篇文章的介绍,希望能给大家提供一些在 UI 自动化方面的想法和思路。