引言
Android白盒测试覆盖率低的最主要原因,是大部分人都没有测到Android层,只测试了Java层部分,导致覆盖率低。因为大部分代码都在Android层。
亲,你是不是认为Android层的都测试不了尼,下面来看看吧
一 、Android层 可测性预研
1、Application可测性
Application级 是app全局共享的,通常用作数据传递,数据共享,数据缓存等。
因此,application级也是需要测试的,也是可测的。
样例代码如下:
验证版本号等等。
结论:Application级可测。
2、Activity可测性 ##
Activity可测性,主要预研,Activity里的:
1)private方法是否可测(不管被UI层调用的);
2)public 方法是否可测;
3)static 方法是否可测;
4)相关依赖是否可mock;
5)接收参数的Activity是否可测。
(1)private方法是否可测(不管是否被UI层调用的)。
业务代码中,private 方法通常被UI层调用,如下,被按back键时调用,如果按一般的测试java层是测试不到的。
如果你又想通过反射来测试:
要么你要得到当前业务app的该Activity实例,这个一般人是得不到的;
要么又想自己创建一个Activity来反射,但因android机制,即使你能创建出来,那这个Activity实例也不是业务app的该Activity实例。方法依赖其他逻辑照样测试不了。
可测性预研结果:可测。
测试样例代码如下:
先创建Activity的rule,然后通过rule.getActivity()获得实例。再获取该page。
有了对象,再反射调用就可以了,最后验证结果。
结论:Activity里的 private方法可测。
(2)public 方法是否可测。
(3)static 方法是否可测。
得到了实例,public和static均可以直接调用。
结论:Activity里的public 返回和static返回 可测。
(4)相关依赖是否可mock。
如下,purify业务app中,有第三方库的调用,这个调用里存在异步线程的处理。如果不mock,将不能得到正确的验证结果。
mock后的测试样例代码如下:
结论: 可Mock。
(5)接收参数的Activity是否可测。
如下,业务代码中,Activity启动时含有对启动intent是否有参数的逻辑。如含有对应参数,则上报一个统计点。
那么这个逻辑是否可测呢?
测试样例代码如下:
结论:接收参数的Activity可测。
至此,Activity里大部分逻辑都可测。
3、Service可测性
Service进程是app的一个单独进程,里面的不少逻辑,同样很多人人为不可测,那实际尼?
Service测试样例代码如下:
结论:Service可测。
4、广播Broadcast可测性
假设有一业务广播如下:
同样的,广播测试样例代码如下:
结论:broadcast可测。
二、java层单元和接口测试
纯java的逻辑测试,是大部分人做的,但这里所要说的,还包含一些依赖android环境的测试,比如,一个java方法依赖了android 的context,SharedPreferences等,可能有人就又认为这些测不了了。
单元测试:包含类的测试,主要测试多条件入参的测试,比如一个类方法 不同参数的传入测试。
接口测试:包含调用链路的测试,包括不同层次的链路调用。主要测试集成路径,不同参数的路径。
单元测试和接口测试,大部分做过白盒的都懂,这里就不细说。
主要说说 ,涉及Android部分的如何测试。
如下业务代码:
被测方法isNeedUpdateTrashRules –>调用了 retrieveTrashRulesUpdateMillis,而该方法用了Android环境SharedPreferences。
思路还是:mock掉,然后塞进去,最后验证。
测试样例代码如下:
三、异步线程可测性
被测方法调用了异步代码时,测试代码将无法正确的验证结果。导致用例失败或不可测。
因此,如何能让异步代码可测,也是如何让现有代码更可测的一部分。
异步线程的可测性思路。
思路一:通过CountDownLatch来实现,这个需要改业务代码,一般不怎么用。
思路二:mock掉异步对象,反射进去,当执行异步时,通过调用拦截获得thread对象,立即调用thread.run()。
思路三:new thread的方式,一般都和回调一起,先mock掉父调用,拦截回调,直接调用回调。
下面说下使用较多的 ExecuteService异步和handle.post的测试样例:ExecuteService异步的测试样例。
业务有如下图异步线程:
测试样例如下:
handle.post() 样例:
如下,业务代码使用了内部handle来处理消息,当执行到handle.post() 因为是异步,测试用例无法获取正常结果。
然而 handle.post有如下多个方法,难道每个都要针对性处理么?那这样比较麻烦。
翻看Android 源码handle.post部分,发现如下调用关系:
各方法最终都调用了 handle.sendMessageAtTime(Message,long),因此只要mock这个就可以了。
测试样例代码如下:
四、函数回调可测性
思路:依然是通过mock,并拦截函数调用,获取对象直接调用。
1、参数传入回调方式可测性
如下业务代码:原始回调被包装了3次回调,最后以参数方式传入。
测试样例代码:
五、Mock简单举例
Android 白盒测试mock,支持多种框架,常用的用mockito和PowerMock。
其中静态方法的mock只能用PowerMock。
Local Unit Tests :可用mockito和PowerMock。
Instrumented Tests :只能用mockito。
1、Android环境Mock
Android Context的mock:
2、Android API Mock
3、普通Mehod和Field Mock
普通对象的method调用:
普通Field的mock:
4、静态Method和Field Mock
静态要mock的,需用PowerMock。网上很多资料。这里就不细说了。
六、反射调用与执行
但遇到 private 标示的逻辑时,不能直接调用。但又想把他测试覆盖,可用反射来执行。
一般主要在:
1、不好从上层介入的地方:private 标示的逻辑,被UI层调用,比如被onclick事件调用,你不好从上层介入。
2、多参数或分支较多:private标示的逻辑,入参较复杂,内部分支和逻辑较多,想以单测函数来先保证正确性。
业务代码的反射和 lib库代码的反射,用法差不多。就写在一起了: ReflectUtil是已经封装好的反射工具类。
七、业务代码直接调用
在模式和方案选型时,是否能直接调用业务代码,也是一个衡量项。最好是能直接调用。能省事省力。
1、业务代码直接调用
未完待续……
关注微信公众号:腾讯移动品质中心TMQ,获取更多测试干货!