客户端单元测试-Android

单元测试的作用

为了使工作完成的更加轻松,设计更加的完善,减少调试的时间提高代码的质量。

什么是单元测试

单元测试是开发者编写的一小段代码,用于检验被测代码的一个很小的、很明确的功能是否正确,通常而言,一个单元测试是用于判断某个特定条件(或者场景)下某个特定函数的行为。

Android 端的单元测试

什么是mvp

  -->区分mvc,mvp,mvvm

关於单元测试

对於单元测试需要了解

  • Android Studio的test和AndroidTest
  • AndroidJUnitRunner:一个兼容Junit4的Andriod单元测试框架
  • Mockito:单元测试利器
  • Espresso:支持UI测试的单元测试框架

MVP各层的单元测试选型

在项目中,MVP各层所使用的单元测试框架如下图所示:

             

  • P层:不需要任何Android环境,因此使用Junit测试即可
  • V层:使用Google强大的Espresso进行UI的测试
  • M层:涉及到数据库相关操作,因此需要依赖Android环境,使用AndroidJUnitRunner进行测试

各层的配合测试

推荐谷歌官方mvp实例项目  https://github.com/googlesamples/android-architecture

接下来我们以TO-DO List页面(TasksActivity/TaskFragment)中加载任务列表功能为例,此场景的功能界面如下图所示:

                     blob.png

P层测试

P层在这里就只做了loadTask()事情

                  blob.png

                                                          loadTask()时序图

从图中看loadTask()执行逻辑,

1.调用V层开启加载框 -->2.调用M层获取列表数据 --> 3.M层以回调模式返回数据 -->4.调用V层关闭加载框 --> 5.调用v层显示列表数据

这5个步骤里面,每个步骤的逻辑是否精准是V层和M层需要测试的,对于P层来说,它只需要确认这5个步骤如期调用就行.

这里使用mockito,在测试类TasksPresenterTest中


总结:让Presenter充当个合格的皮条客,去调用其他两层的逻辑,在假设其他两层代码逻辑都是正确的前提下,做一些mock测试,尽可能覆盖所有逻辑路径。

V层的测试

对于V层在加载任务列表中的逻辑就只有显示加载框-->隐藏加载框 →显示列表数据

既然这么简单那么我们验证添加任务到显示任务列表

流程:点击添加按钮-->打开软键盘输入标题和描述-->保存代办任务-->返回任务列表--> 验证输入的代办任务是否存在

通过Espresso可以模拟这些步骤,并进行验证。这个测试类是TasksScreenTest

1.首先是添加任务和输入标题描述,保存代办任务

                   

2.返回任务列表就需要刷新列表–其实也就是刷新数据–相当于点击选择all标签

                  

 

3.验证刚刚的任务是否存在

                 

 

整个V层测试方法:

                 

 

总结:Espresso好强大,V层测试是站在用户角度去测试每一个操作,它也不止测试了V层,还测试了一部分的P层和M层

M层测试

关于Model层的测试,首先要了解下该项目中,model层的设计,类层次如下图所示:

               blob.png

  • 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各层之间的职责以及对应的测试内容,接下来做个总结,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中。

总结

写单元测试前期虽然比较复杂和麻烦,但是这样谷歌官方的写法思路是非常清晰的,非常值得学习,可以减少很多测试成本,减少错误率。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章