为了使工作完成的更加轻松,设计更加的完善,减少调试的时间提高代码的质量。
单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确,通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。
-->区分mvc,mvp,mvvm
对于单元测试需要了解
在项目中,MVP各层所使用的单元测试框架如下图所示:
推荐谷歌官方mvp实例项目 https://github.com/googlesamples/android-architecture
接下来我们以TO-DO List页面(TasksActivity/TaskFragment)中加载任务列表功能为例,此场景的功能界面如下图所示:
P层测试
P层在这里就只做了loadTask()事情
loadTask()时序图
从图中看loadTask()执行逻辑,
1.调用V层开启加载框 -->2.调用M层获取列表数据 --> 3.M层以回调模式返回数据 -->4.调用V层关闭加载框 --> 5.调用v层显示列表数据
这5个步骤里面,每个步骤的逻辑是否精准是V层和M层需要测试的,对于P层来说,它只需要确认这5个步骤如期调用就行.
这里使用mockito,在测试类TasksPresenterTest中
总结:让Presenter充当个合格的皮条客,去调用其他两层的逻辑,在假设其他两层代码逻辑都是正确的前提下,做一些mock测试,尽可能覆盖所有逻辑路径。
对于V层在加载任务列表中的逻辑就只有显示加载框-->隐藏加载框 →显示列表数据
既然这么简单那么我们验证添加任务到显示任务列表
流程:点击添加按钮-->打开软键盘输入标题和描述-->保存代办任务-->返回任务列表--> 验证输入的代办任务是否存在
通过Espresso可以模拟这些步骤,并进行验证。这个测试类是TasksScreenTest
1.首先是添加任务和输入标题描述,保存代办任务
2.返回任务列表就需要刷新列表–其实也就是刷新数据–相当于点击选择all标签
3.验证刚刚的任务是否存在
整个V层测试方法:
总结:Espresso好强大,V层测试是站在用户角度去测试每一个操作,它也不止测试了V层,还测试了一部分的P层和M层
关于Model层的测试,首先要了解下该项目中,model层的设计,类层次如下图所示:
TasksLocalDataSource:负责本地数据库增删改查操作
TasksRemoteDataSource:负责网络请求(该项目中用handler.postDelayed()延时来模拟网络请求)
TasksRepository:相当于整个Model层的门面,根据逻辑判断决定数据来自于本地数据库或是网络。Presenter层只与它打交道。
根据以上分析,可见对Model层的测试要完整的覆盖这三个类。
1.我们先看门面TasksRepository的测试,先看看这个类中有关获取待办任务列表的流程图:
所以对于TasksRepository来讲,测试的内容主要是验证1,2,3的逻辑是否在相应的输入下覆盖到位,对于1,2,3的数据准确性无需关心,
由各自DataSource去验证,因此它的测试与Android环境无关,用Junit+Mockito测试。要完整覆盖的话,需要多个测试case,
篇幅有限,这里只讲第2种。这个测试类是TasksRepositoryTest,代码如下:
2.接下来是是TasksLocalDataSource的测试。该测试与数据库有关,因此依赖于Android环境,且要验证数据存取的准确性,
因此需要做一些断言,使用AndroidJUnitRunner进行测试,这个类是TasksLocalDataSourceTest
,代码如下:
3.最后来看看跟网络请求相关的TasksRemoteDataSource的测试
Google并没有对这个类本身进行测试,但是对其他层依赖网络请求数据进行测试的场景做了支持。试想一下,通过上面的分析,
我们知道View层是真刀真枪的在模拟用户的操作进行测试,如果某个测试case需要发起网络请求,此时我们不知道何时才能返回数据,
且由于网络状况等原因可能导致请求失败,种种不确定因素下,是不可能完成一个测试的,解决的办法很简单,
就是对网络请求进行Fake,这个类是FakeTasksRemoteDataSource,原理便是当需要用到TasksRemoteDataSource时,
不会真正使用该类,而是注入FakeTasksRemoteDataSource,返回事先定义好的数据。
MVP各层之间的职责以及对应的测试内容,接下来做个总结,MVP测试架构图
View层
职责:MVP模式下,View层终于扬眉吐气了,View本身该做的事情都能做了,比如UI布局,数据渲染,点击按钮交互等等
测试方式:以正常小QA的测试思维方法,就可以来定义这一层的测试方式,测试过程中需要真机或模拟器,并做真实的操作。
测试选型:依赖于Android环境,用谷歌强大的Espresso+AndroidJUnitRunner,Espresso用于模拟和验证各种各样的UI操作,代码存放于AndroidTest中。
Presenter层:
职责:这一层是拉皮条的,负责M和V层的对接,所以有较少的处理输入输出的机会,他只用来控制逻辑,去调用相应的Model和View的逻辑。
测试选型:他的职责决定了他很少去断言输入输出,测试逻辑覆盖的路径是否正确即可,因此他与Android环境无关,用Junit+Mockito测试即可,代码存放于test中。
Model层
职责:负责数据的存取,数据可能来自于网络、数据库和内存
数据库增删改查:需测试数据存取的准确性,依赖Android环境进行测试,因此使用AndroidJUnitRunner,代码存放于androidTest中
网络请求:不测试真实的网络请求,但提供了Fake供其他层调用测试。
封装的门面类:决定了数据的来源和去向是来自于本地数据库 or 网络 or 内存,此为真正对其他层暴露的Model类。此类不做数据准确性的验证,只做mock测试,验证覆盖路径。
UT选型Junit+Mockito,代码存放于test中。
写单元测试前期虽然比较复杂和麻烦,但是这样谷歌官方的写法思路是非常清晰的,非常值得学习,可以减少很多测试成本,减少错误率。