对于一个硬件连接项目,底层gg终于开发出mock流的库,后续的许多测试都可以进行了。对于自动化,在云测的机型兼容和本地的主流程验证其实还是起到比较重要的作用的,但是app的UI自动化这个东西本身就是一大坑,无论是成功率还是投入的人力成本,发展到现在也依然是一个问号,不过东西还是要做的,至少对于个人帮助还是很大的… …
本篇分享的是在公司已经完成的一个二次开发的自动化框架,基于as + robotium + uiautomator + spoon。目前已经是在MQC和本地顺利运行的,当然了,一个人的精力和技能是有限的,有很多地方肯定是有缺陷,以及很多想法是一个人短期内难以完成的,so有错误的地方请指出。
PS:这篇是给公司内部分享用的,有的地方不可避免屏蔽掉,请不用太在意。
分支地址:dev_cz
和官方的结构一致,无区别,唯一的区别是使用HoloTestRule替换ActivityTestRule,在@Before内初始化solo变成在声明HoloTestRule的时候已经帮你做的,接下来只要get就ok了,另外使用holo替代solo,以便使用一些新的特性。
都是基于android studio的。
Build.gradle:
为了和发布版本区分,fake测试程序单独放在一个渠道包那里
当然,最后面签名文件的写法需要变更一下,否则spoon插件会读取不到(这是gradle的bug?)
和官方的结构一致,无区别,唯一的区别是使用HoloTestRule替换ActivityTestRule,不需要初始化solo,直接HoloTestRule.getHolo即可。
或者通过命令行gradlew spoonFakeAirDebugAndroidTest执行(命令你可以通过gradlew task看到)
可以看到报告里面是有两个机型的,也就挖掘了我们一开始说的特点之一,在本地支持多设备并行测试。(可以点开例子)
国内平台也有挺多的,腾讯wetest、百度mtc、testin等等,那么选择MQC的主要原因是:
- 支持自定义脚本
- 更新比较及时,覆盖度比较不错
- 其他云测支持的MQC也都支持
- 支持jenkins插件(beta)
- 列表内容
- 便宜… …
所以本着用最少的钱办最多的事,就暂时定下了MQC平台。
首页就是这样了,我们是专业的,看这个好不好看首页没啥用,我习惯是直接奔到文档说明
那么功能看起来是非常多的啊,除了对支持还支持IOS,平台支持性比较完备。
那么我们主要使用的是自定义脚本执行功能,对应叫做功能测试。
同时,MQC支持jenkins插件集成的,当设置恰当的触发条件后,编译完成apk包,MQC支持把待测包直接上传到MQC平台,然后自动验证,然后生成一个分享连接,我们只需要把结果连接邮件发送给相关人员就行了。想法很美好,现实很残酷。
持续集成目前估计还在开发阶段,只有体验套餐,然后测试结果是这样的:
心里:##$%^^&&&&&&@@!!!
好吧,反正是免费的,人家随机的机型压根就不给你5.x以上的手机,都是差不多淘汰的低端机,所以自动上传验证这块,想想就好了,默默等人家平台支持吧。
Ok,云测部分就这么多,云测最主要解决的是公司某些机型覆盖不足的问题,做的主要是兼容性功能验证,一些复杂的逻辑性验证,只能通过本地的自动化去完成。
androidJunitRunner
Android官方推荐的runner,我们以前使用instrument框架的时候都是基于junit3的,这个runner让instrumentation支持junit4,然后还添加了一些annotation。Robotium
世界上使用人数最多的第三方自动化框架,可自定义功能最强大,历史最悠久,当然了,官方的espress也不差就是了。笔者这里是和uiautomator一块用了,主要是为了解决跨进程问题。Spoon
第三方的展示报告,支持多设备并发运行,支持截图展示,界面简介,非常适合UI自动化的可视化展示。
接下来直接打开项目
首先看目录:
其中最核心的类就三个:
HoloTestRule
负责启动activity,对失败用例进行重试和截图
HoloRunner
重试用的自定义runner,调用runchild方法执行
Holo
Solo类的子类,添加一些特性,和solo一样,是进行自动化操作调用的一个中介者
这个就是我们自定义的rule,是ActivityTestRule的子类,用来替代ActivityTestRule。使用的时候通过@Rule声明这个变量。
这个类的主要作用:
1.通过instrument的startActivitySync启动传入的class,返回一个activity
2.启动activity后,实例化Holo对象
3.Trycatch执行过程,如果捕获到异常,那么开始重试机制,并开启失败截图设置
下面我们逐个讲解这些特性
首先看下HoloTestRule的继承关系:
(I)TestRule
UiThreadTestRule
ActivityTestRule
HoloTestRule
HoloTestRule是ActivityTestRule的子类,所以也实现了TestRule接口。TestRule接口是juni4添加的新特性,相当于在执行用例之前提供给你一个方法去自定义你自己的操作。ActivityTestRule重写了apply方法,主要完成了对activity的启动
当然了,启动方法还是用了instrument框架。
同时,ActivityTestRule是继承UiThreadTestRule,UiThreadTestRule添加的功能就是保证后续所有的执行都是在UiThread里面的:
保持在ui线程的方法还是用了instrument的runOnMainSync这个阻塞方法。
最后,我们自定义的HoloTestRule是他们的子类,通过重写apply方法又额外为自动化添加了一个新的功能—失败重试和截图。
eg:通过重写apply方法,传入和返回一个实现了statement接口的功能对象,沿着类
UiThreadTestRule–>ActivityTestRule–>HoloTestRule一直传递下去,网上看到有人说这是装饰者模式,给子类动态添加功能的模式,笔者也只是对设计模式知道一点点,这里就没法过多解析了。
下面继续看下失败重试的代码:
代码如下:
通过try…catch…把前面执行的步骤做一个捕获操作,如果运行异常,那么我们认为是用例执行失败了,那么可以知道是捕获了任意条件下执行失败的情况(so,也包括断言,找不到控件神马的),当然你可以根据异常的类的不同自行定制了。
好了,捕获到了用例执行失败,下一部需要重试,代码如下:
这里作用有两个:
1.读取HoloConfig配置重试次数,有需要的可以直接去修改
2.生成HoloRunner类,这个类是负责用例执行的。
3.设置一个失败截图标志,这个标志为true代表后续执行操作都会保留截图,也就是我们报告看到的失败截图。
HoloRunner是给HoloTestRule用的重试类,继承自BlockJUnit4ClassRunner,后者是junit4的默认runner,继承关系如下:
Runner
ParentRunner
BlockJUnit4ClassRunner
HoloRunner
这个类调用了一个方法:runChild,传入Description描述信息。
runChild这个方法是实际运行测试用例的地方,它的实现是在BlockJUnit4ClassRunner中:
这个方法会解析@Rule@Before@After和@Test标签,最终执行method.invoke方法,开始测试用例执行,所以想要再一次执行某用例,只需要声明一个runner然后执行runchild就行了。
Eg:用例执行的入口是runner类的run接口,然后实现类是ParentRun,一个重要的方法是classBlock,会取得@BeforeClass@AfterClass@ClassRule标签的方法,并且会调用runChildren方法,这个方法会拿到所有测试方法,然后执行runchild方法,这个runchild方法就是单个用例实际执行的方法了。
作为对solo类的扩充,实际自动化的操作都是经过这个类的,这个类是跟随HoloTestRule一起初始化的,所以在@Before可以直接调用getHolo方法返回holo对象。除了原有的solo方法外,还封装了一些常用的方法,比如经常使用的断言和操作,具体实现类看类名就猜得出了。
方法一:
这也是一开始使用的方法,利用solo类的序列截图功能,然后在HoloRunner执行的前后开启一系列的截图,这也是我为什么想方设法把holo类塞进HoloTestRule里面去,弄了一个下午然后发现无法截图…然后在google上找到了一句话
于是不得不使用方法二,比较折中的办法,个人还是喜欢序列截图的,还是等官方修复吧,所以代码还是遗留在里面。
实现是这样的,因为后续需要把截图放整合在spoon的报告里面,所以使用startScreenshotSequence的时候只要把规则设定成spoon的规则就行了,spoon的规则可以在Spoon代码里面的filesDirectory方法看到,好吧,这里写出来,规则是这样的:
/sdcard/app_spoon-screenshots/testcase.mytestcase/testMethod
于是,在初始化holo的时候,传入对应的规则就行了:
方法二:
这个方法直接使用了Spoon.screenshot方法,思路是在fail用例的时候给一个重试标志,然后在通过HoloRunner执行重试的时候,在调用holo进行自动化操作的时候,为每一步执行进行截图操作,然后跑完在finally方法把标志清除。这个方法相当于变相失败截图了,这也是为什么我们要自定义solo的原因之一,当然,你也可以通过HoloConfig配置强制开启全部用例执行的截图,但是这样会比较慢,看取舍吧。
截图问题
不支持opengl的截图,这个问题暂时没办法解决,需要再研究下
部分机型授权问题
目前使用的方案是跨进程点击,是等待界面上出现授权弹框后点击允许操作,悲剧的是国内ROM过多,授权弹框定制的面目全非,有的手机甚至还无法dump下当前界面,有的手机非要你打开开发者调试,手动授权才行… … 但是目前发现云测运行兼容测试的时候,是自动获取权限的,有时间和他们沟通一下,不过理不理我就另说了…还有一种方式spoon的gradle task里面发现一个字段grantAllPermissions,有空可以研究一下到底怎么做的,也算是一个思路吧。
太慢了
这个是框架本身的缺点了,Espresso框架最大的优点是解决的UI线程同步的问题,在别人还在考虑不同机型sleep多长的时候它已经跑完了,so后续可以考虑迁移到Espresso框架。
对性能指标的监控
在弄清楚了junit4的运行流程后,我们可以在合适的地方获取合适的性能值
对内存泄漏的上报
之前分析过LeakCanay的流程,知道了有一个线程负责记录和生成报告的,我们完全可以截取那个线程,然后发现泄漏直接发送邮件,或者本地自动提单的操作,这些都是可行的。