2020年的1月15号,我入职了百度,成了一名测试开发实习生,到现在为止,也已经有整整一年的时间了。
这一年发生了很多事,因为疫情原因在出租屋里吃了一个月的泡面,因为不返校而得以一直实习,先是在百度,然后是在字节,还认识了一个我坚定地觉得可以相伴一生的人。一年前的我只有后端开发的经验,对测试领域一无所知,误打误撞进了客户端测试的领域,并且一直做了下来,经历了很多,也算是有所思考和沉淀吧。下面就谈一谈我经过一年的工作,对于客户端自动化测试手段的思考。
UI自动化
UI自动化,这几乎是每一个客户端方向的测试工程师在学习或者团队内发展自动化测试的时候的第一个方向。大家提到自动化测试,一般而言,如果是客户端领域,指的就是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驱动这个角度出发,可以将自动化框架分成两大类:注入式、非注入式,那么这两种各有什么优劣势呢?最大的不同点在于稳定性和性能:
- 稳定性:记得刚用Appium的时候,测试机是一台vivo低端机,case编写好之后运行的时候,稳定性非常差,首先是定位元素很慢(这和设备性能也有关系),然后是经常跑着跑着,从一个case开始,后面的全部case都失败了,然后查了日志才发现,vivo魔改OS,把耗电高的进程——也就是测试服务的进程给杀了。但是如果UI驱动是注入式的,那么测试服务和App本身就会“同生共死”,只要App进程不死,测试进程也不会死,同时注入式的UI驱动也会极大增大元素定位的速度
- 性能:因为测试服务是注入进App进程里的,所以或多或少都会影响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自动化是否合理,在每一个测试团队推行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提取出公共场景解耦等等…
这些是对开发者的要求,从对框架的要求来看,什么算是“优秀的自动化框架”呢?我认为主要有以下几点:
- 稳定性:尽量不出现因为框架不稳定导致case失败的情况
- 易维护性:将case提取出一步一步的路径节点进行解耦复用
- 易扩展性:例如通过sdk或者注入得到一个统一格式的ui tree,框架层进行元素查找,再调用对应driver进行操作,不管以后接入什么终端,只要提供sdk和driver,框架层不需要作出任何修改
- 易编写性:我个人看法是“去IDE化”,有些框架会提供本地IDE给开发者编写case,但是环境的包袱太重,可以采用云IDE,在云端利用云真机编写/录制生成case,也不需要开发者在本地配置各种冗杂的环境,同时利用mapping文件云端解混淆,也可以做到对混淆无感知
- 可分布式执行、并发执行:可以将case打到设备集群上并发执行,减少测试时长,也能增大机型/系统覆盖率
UI自动化2.0
从百度一路走来,在UI自动化方面踩了很多坑,刚写case的时候,没有注意case的解耦、复用,导致维护起来非常消耗人力,于是后来进字节之后,将case提取出公共场景进行复用,比如评论和买车都需要登录,就把登录场景单独拎出来给评论case、买车case复用,这样如果以后登录case页面做出改动,也只要修改一个case就行。字节自研了一个自动化框架,叫“Shoots”,采用PageObject设计模式,框架设计和稳定性都很不错。但是编写这个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自动化访问特定接口进行接口健壮性测试。还可以从很多角度优化自动化脚本编写的体验和效率,比如元素id变了之后自动修复脚本、提供历史截图和控件树实现不需要连手机就能编写case。
客户端自动化的未来:AI Test
现在很多公司都在AI Test方面进行探索,客户端的话主要是自动测试生成(Automated Testing Generation)技术,编写case不再需要工程师,而是可以自动生成和维护case,目前在稳定性测试方面落地得比较好(https://github.com/bytedance/Fastbot_Android )。case生成主要有两种思路,一种是提取出界面的控件,对Activity/ViewController进行bfs/dfs/A*搜索,还有一种是利用线上用户的行为提取行为链进行case生成。我在我们组负责过一个AI Test工具的落地,crash召回率有40%左右,收益还是挺不错的。