2020 年的 1 月 15 号,我入职了百度,成了一名测试开发实习生,到现在为止,也已经有整整一年的时间了。
这一年发生了很多事,因为疫情原因在出租屋里吃了一个月的泡面,因为不返校而得以一直实习,先是在百度,然后是在字节。一年前的我只有后端开发的经验,对测试领域一无所知,误打误撞进了客户端测试的领域,并且一直做了下来。一年,和各位大佬比起来不过是刚刚入门,但是经历了一些之后也算是有所思考和沉淀吧。下面就谈一谈我经过一年的工作,对于客户端自动化测试手段的思考。
UI 自动化,这几乎是每一个客户端方向的测试工程师在学习或者团队内发展自动化测试的时候的第一个方向。大家提到自动化测试,一般而言,如果是客户端领域,指的就是 UI 自动化。在我刚入职百度的时候,我的方向就是双端的 UI 自动化。
UI 自动化,就是将用户的行为通过 “自动化测试框架” 进行模拟,例如模拟划动、模拟点击,将一些测试场景抽象出来进行自动化测试,以此达到节约人力的作用。目前市面上的自动化测试框架非常多,例如appium
、totoro
、QTA
、uia
、uia2
等。一些大厂基于不同的侧重点也会进行自己的自动化框架的研发,比如携程基于行为驱动(BDD)的自动化测试,网易提出的用图片就能编写 case 的Airtest
,腾讯从稳定性和多端支持角度提出的QTA
等。不同的框架出发点不一样,有的是为了稳定性,有的是为了编写时上手的简单性,有的是为了可扩展性,但是框架的原理大同小异,可以认为一个完整的 UI 自动化框架一般由两部分组成:UI 驱动和设备驱动。
UI 驱动用来执行对元素的操作,比如查找元素、点击元素,设备驱动用来执行对设备的操作,比如冷启 app、关机等。设备驱动的话安卓端的开源方案有adb
,iOS 端的开源方案有 facebook 的wda
和idb
,一些公司也会进行自己的设备驱动的开发,比如字节自研的BDC
。UI 驱动的话,主要思路有两种,一种是注入式,一种是非注入式。像上手最简单的Appium
采用的就是非注入式方案,查找和操作 UI 是从 App 进程外对 App 进行操作的。非注入式的 UI 驱动有:安卓端的UIA
,iOS 端的Instrumentation
、XCUITest
等。腾讯的QTA
和字节的Shoots
采用的就是注入式方案,对 app 进行重打包,往包里注入一个 server,在 app 启动的时候,启动一个对应的网络服务,通过这个网络服务提供测试接口。
因此,从 UI 驱动这个角度出发,可以将自动化框架分成两大类:注入式、非注入式,那么这两种各有什么优劣势呢?最大的不同点在于稳定性和性能:
UI 自动化不光光是 UI 自动化,可以以 UI 自动化为入手点,将客户端测试的很多方面通过自动化落地,比如埋点自动化测试、性能自动化测试等。
功能回归测试:这是 UI 自动化最基础的用途,因为一般新功能的话手工回归更稳妥,所以重复性高的回归 case 采用自动化测试更为合适,可以将功能回归测试放在持续集成流程里,每出一个新包/每监听到一个新 push 就执行一遍,保证新增代码不影响旧功能或者核心功能
埋点测试:埋点上报的本质是发送 http 请求,埋点测试也是进入到某个 App 场景后进行埋点数据校验,那么可以利用一些技术手段,劫持 App 发送的埋点数据,然后通过 json schema 进行自动化校验。json schema 可以自己编写,也可以在录制 case 的时候进行生成
性能测试:有时候需要把自己产品的一些核心场景与竞品横向对比,做性能评测。性能主要有以下几个指标:耗时、cpu、fps 等。如果不用自动化手段,需要手动录屏,然后手动拆帧获得耗时数据,安卓端通过 adb 命令获得 cpu、fps 等数据,iOS 则更为复杂。用自动化可以实现自动录屏,自动分帧,自动选取开始帧结束帧计算耗时,还可以集成一些工具,自动获取 cpu、fps 等信息。
服务端接口防劣化:假如有这样一个场景——一个服务端接口因为种种原因挂了,导致线上功能异常或者页面白屏,此时一般只能通过用户反馈或者服务端报警才能知道接口出了问题,这个通过 UI 自动化也可以无人值守的监控:每隔一定时间跑一遍核心接口相关的 UI 自动化,同时用一些 cv 工具对页面进行白屏检测,那么如果出现问题,就能及时报警
UI 自动化是否合理,在每一个测试团队推行 UI 自动化的时候都会遇到这样的争议。团队 leader 会觉得,自动化收益不明显、反而更耗费人力,业务测试 peer 会觉得,这是给自己工作增负,而且落地的好也只不过是给他人做嫁衣。这些矛盾的关键点在于:UI 自动化能否真正给团队带来效益?
UI 自动化其实难度很大,有一张很经典的三角形示意图,最底层面积最大的是Unit Test
,cost 最小,收益最大,最顶端面积最小的就是UI Test
,cost 最大,收益最小。做 UI 自动化,不光要求对框架的原理、app 的代码都有所了解,还要求对产品和业务非常了解。不清楚自动化框架的原理,case 出了问题无法排查。UI 自动化本质上也算是一种白盒测试,不了解客户端的知识也很难下手,比如不清楚 iOS 的证书体系,就无法为 app 注入代码执行 case,不了解安卓 UI,就会不理解为什么 QTA 对 UI 层的抽象设计。假设这些都了解了,也需要很熟悉产品业务。例如哪些场景有 ab test,ab test 如何解决,如何尽量减少对线上数据的影响等。同时,如何让编写的 case 稳定性更好、维护性更好也很有学问,避免使用坐标点击、考虑不同分辨率设备的 UI 界面、将 case 提取出公共场景解耦等等...
这些是对开发者的要求,从对框架的要求来看,什么算是 “优秀的自动化框架” 呢?我认为主要有以下几点:
从百度一路走来,在 UI 自动化方面踩了很多坑,刚写 case 的时候,没有注意 case 的解耦、复用,导致维护起来非常消耗人力,于是后来进字节之后,将 case 提取出公共场景进行复用,比如评论和买车都需要登录,就把登录场景单独拎出来给评论 case、买车 case 复用,这样如果以后登录 case 页面做出改动,也只要修改一个 case 就行。字节自研了一个自动化框架,叫 “Shoots”,但是编写这个 case 的上手门槛非常高,要求 QA 很熟悉 python 和客户端环境,虽然它提供了一个本地 IDE,但是 case 还是需要自己新建类自己写,非常不适合代码能力没那么高的业务同学用,于是师兄就带我开发了一个测试平台,设计了一套类似 uia2 的脚本语法,将生成的脚本用 jinjia 模版翻译成 Shoots 的代码,这样就曲线救国实现了 case 的自动生成。云真机的前端渲染是开发 app 的 SDK 提供截图 +ui 树(Native 控件使用原生代码获取 ui 树,webview 界面通过向 webview 注入 js 获取 ui 树),发给服务端生成预览。
UI 自动化的 2.0,意味着要放宽思路,很多技术都可以应用于 UI 自动化中。例如通过 hook,对一些没有 id 的控件自动生成 id(https://github.com/yulingtianxia/TBUIAutoTest ),降低元素定位难度。例如将一些 cv 算法用于 UI 自动化测试过程中判断界面是否存在花屏/白屏,图像对比、模版匹配算法对比两幅图像是否一致,可以用于性能测试时获取开始帧、结束帧,也可以判断 ui 自动化测试的结果是否符合预期。还可以结合fuzz test
,利用 UI 自动化访问特定接口进行接口健壮性测试。
现在很多公司都在 AI Test 方面进行探索,客户端的话主要是自动测试生成(Automated Testing Generation
)技术,编写 case 不再需要工程师,而是可以自动生成和维护 case,目前在稳定性测试方面落地得比较好(https://github.com/bytedance/Fastbot_Android )。case 生成主要有两种思路,一种是提取出界面的控件,对 Activity/ViewController 进行 bfs/dfs/A* 搜索,还有一种是利用线上用户的行为提取行为链进行 case 生成。