Android单元测试介绍
处于高速迭代开发中的Android项目往往需要除黑盒测试外更加可靠的质量保障,这正是单元测试的用武之地。单元测试周期性对项目进行函数级别的测试,在良好的覆盖率下,能够持续维护代码逻辑,从而支持项目从容应对快速的版本更新。
单元测试是参与项目开发的工程师在项目代码之外建立的白盒测试工程,用于执行项目中的目标函数并验证其状态或者结果,其中,单元指的是测试的最小模块,通常指函数。打包时单元测试的代码不会被编译进入APK中。
Android单元测试框架
Android的单元测试可以分为两大类:
android中的单元测试基于JUnit4,可分为instrumented测试和本地测试。
1.module-name/src/androidTest/java/.
该目录下的测试代码需要运行在android设备或模拟器下面,因此可以使用android系统的API,速度较慢。
优点:直观,运行在真机上
缺点:运行速度慢,需要编译安装app
代表框架:UIAutomator,Robotium,Espresso,Macaca,Appium等
2.module-name/src/test/java/.
该目录下的代码运行在本地JVM上,其优点是速度快,不需要设备或模拟器的支持,但是无法直接运行含有android系统API引用的测试代码。
优点:速度快,使用简单,方便
缺点:不够直观,比如有硬件相关(比如wifi,Bluetooth等),兼容性问题无法测试出来。
代表框架:Mockito,EasyMockito,Jmockit Powermock,Robolectric等
Android单元测试以上分别执行在JUnit和AndroidJUnitRunner的测试运行环境,两者主要的区别在于是否需要android系统API的依赖。
在实际开发过程中,我们应该尽量用JUnit实现本地JVM的单元测试,而项目中的代码大致可分为以下三类:
- 强依赖关系,如在Activity,Service等组件中的方法,其特点是大部分为private方法,并且与其生命周期相关,无法直接进行单元测试,可以进行Ecspreso等UI测试。
- 部分依赖,代码实现依赖注入,该类需要依赖Context等android对象的依赖,可以通过Mock或其它第三方框架实现JUnit单元测试或使用androidJunitRunner进行单元测试。
- 纯java代码,不存在对android库的依赖,可以进行JUnit单元测试
UnitTest框架选择
要较好的完成本地单元测试功能,就需要mock对象来进行操作,
什么是Mock?
Mock是虚拟对象,是为了模拟真实对象而创建的,这些虚拟对象的行为是可控的。为方便理解,可以把Mock视为『汽车碰撞实验』中的『假人』。
假设要对ClassA进行测试,他的依赖如下图:
ClassA的依赖关系
如果进行了mock对象,可以简化为下图:
mock后的依赖关系
为什么需要mock?
1.真实软件架构中的对象行为是不确定的(eg:时间、对象的状态)。
2.真实对象的一些状态又很难构造(eg:网络抖动导致延迟返回)。
3.真实对象在进行某种操作时需要很长时间,影响测试进度(eg:一些数据库的初始化)。
4.实际测试中需要关注真实对象如何被调用,真实对象内部状态如何。
上述的问题都是可以通过引入Mock来解决的。
为什么选择Mockito
- Mockito是StackOverflow社区票选的最佳Java Mock框架。
- Github 中位于Top4的Library,文档多,遇到坑也能较快解决。
- 使用简洁、优雅的API即可写出漂亮的单元测试用例,这些单元测试语法符合自然语言,方便阅读。
Mockito可以完成哪些功能
- 验证Mock对象某函数是否执行,执行次数
- Mock对象某函数有返回值,可以设置返回值。对于void的函数,可以设置抛出异常(即常说的打桩)
- 验证Mock对象函数执行顺序
- 参数匹配(有很多内建的参数,也可以自己实现),这样使得验证函数交互或者设置返回值更加灵活
- 可以捕获参数用于后续进一步验证或设置
Mockito的不足之处
- 现如今比较流行的mcok框架,如Mockito,EasyMock,等都有一个共同的缺点:无法mock static,final,private方法等。
所以这里需要引入PowerMock,他能够完美弥补这个缺陷。 - 在Java中,编写代码面对的只有类、对象、函数,编写单元测试时可以在测试工程中创建一个对象出来然后执行其函数进行测试。而在Android中,编写代码需要面对的是组件、控件、生命周期、异步任务、消息传递等,虽然本质是SDK主动执行了一些实例的函数,但创建一个Activity并不能让它执行到相应的生命周期中(如onResume),因此需要JUnit,Mock之外的框架支持。
当前主流的解决方案是AndroidTest和Robolectric,前者需要运行在Android环境上,后者可以直接运行在JVM上,速度也更快,无需准备Android环境。因此我们选择引入Robolectric。
测试框架方案:
JUnit4是一个Java语言的单元测试框架。多数Java的开发环境都已经集成了JUnit作为单元测试的工具,也是用的最多的一个测试框架,Android Studio创建的工程中就已经集成了JUnit4,且Android单元测试就是基于JUnit4的,默认就加了这个dependencies,
所以最后选择: JUnit4+Mockito+PowerMock+Robolectric的组合来做Android单元测试的工作。
其他
单元测试,集成测试,UI测试
- UI测试是测试到交互和视觉,以及操作的结果是否符合预期。可以通过Espresso,UI Automator等框架,或者人工测试。
- 集成测试是基于单元测试,将多个单元测试组装起来进行测试,实际测试往往会运行慢,依赖过多导致集成测试非常费时。
- 单元测试仅针对最小单元,在面向对象中,单元指的是方法,包括基类(超类)、抽象类、或者派生类(子类)中的方法。
三者的在实际应用中可以通过Test Pyramid(Martin Fowler的总结)来衡量:
Test Pyramid
所以对于测试,在开发过程中,我们(开发者)需要把更多的精力放在单元测试上。
网友评论