蘑菇街支付金融Android单元测试实践(2)

发表于:2016-05-20来源:推酷作者:不详点击数: 标签:单元测试
这里的loadData()方法是void的,它该怎么测试呢?一个最直接的反应可能是,调用loadData()方法(当然,实际可能是通过其他事件触发),然后一段时间后,验证界

  这里的loadData()方法是void的,它该怎么测试呢?一个最直接的反应可能是,调用loadData()方法(当然,实际可能是通过其他事件触发),然后一段时间后,验证界面得到了更新。然而这种方法是错的,这种测试叫集成测试,而不是单元测试。因为它涉及到很多个方面,它涉及到 DataModel、网络服务器,以及网络返回正确时,DataActivity内部的处理,等等。集成测试固然有它的必要性,但不是我们应该最关注的地方,也不是最有价值的地方。我们应该最关注的是单元测试。关于这一点,有一个 Test Pyramid 的理论:

  Test Pyramid理论基本大意是,单元测试是基础,是我们应该花绝大多数时间去写的部分,而集成测试等应该是冰山上面能看见的那一小部分。

  那么对于这个case,正确的单元测试方法,应该是去验证loadData()方法调用了DataModel的某个请求数据的方法,同时传递的参数是正确的。“调用了DataModel的方法,同时参数是。。。” 这个才是loadData()这个方法的“返回结果”。

  Mock的概念以及Mockito框架

  要验证某个对象的某个方法得到调用了,就涉及到mock的使用。这里对mock的概念做个简单介绍,以免很多同学不熟悉,mock就是创建一个虚假的、模拟的对象。在测试环境下,用来替换掉真实的对象。这样就能达到两个目的:

  可以随时指定mock对象的某个方法返回什么样的值,或执行什么样的动作。

  可以验证mock对象的某个方法有没有得到调用,或者是调用了多少次,参数是什么等等。

  要使用mock,一般需要使用mock框架,目前安卓最常用的有两个, Mockito 和 JMockit 。两者的区别是,前者不能mock static method和final class、final method,后者可以。我们依然采用的是Mockito,原因说起来惭愧,是因为刚开始并不知道JMockit这个东西,后来查了一些资料,看过很多 对比Mockito和JMockit的文章 ,貌似大部分还是很看好JMockit的,只是有一个问题,那就是跟robolectric的结合也有一些bug,同时使用姿势跟Mockito有较大的不同,因此一直没有抽时间去实践过。这个希望以后能够做进一步的调查,到时候在给大家分享一下使用感受。

  但是使用Mockito,就有一个问题,那就是static method和final class、final method没有办法mock,对于这点如何解决,我们稍后会介绍到。

  在测试环境中使用mock:依赖注入

  接下来的一个问题就是,如何在测试环境下,把DataModel换成mock的对象,而正式代码中,DataModel又是正常的对象呢?

  这个问题也有两种解决方案,一是使用专门的testing product flavor;二是使用依赖注入。第一种方案就是用一个专门的product flavor来做testing,在这个testing flavor里面,里面把需要mock的类写一份mock的implementation,然后通过factory提供给client,这个 factory的接口在testing flavor和正式的flavor里面是一样的,在跑testing的时候,专门使用这个testing flavor,这样通过factory得到的就是mock的类。这种情况看起来很简单,但其实很不灵活,因为只有一种mock实现;此外,代码会变得很丑陋,因为你需要为每一个dependency提供一个factory,会觉得很刻意;再者,多了一个flavor,很多gradle任务都会变得很慢。关于这种方案,可以参考 这个视频 (https://www.youtube.com/watch?v=vdasFFfXKOY)。

  因此,我们用的是第二种,依赖注入。先简单介绍一下依赖注入这个模式,他的基本理念是,某一个类(比如说DataActivity),用到的内部对象(比如说DataModel)的创建过程不在DataActivity内部去new,而是由外部去创建好DataModel的实例,然后通过某种方式set给 DataActivity。这种模式应用是非常广泛的,尤其是在测试的时候。为了更方便的做依赖注入,如今有很多框架专门做这件事情,比如 RoboGuice 、 Dagger 、 Dagger2 等等。我们用的是Dagger2,理由很简单,这是目前最好用的DI框架。

  关于Dagger2的文章,之前我们群里也分享了不少,但是好像我并没有看到讲述没有关于如何在测试环境下使用Dagger2的文章,这个还是略感遗憾的。离开单元测试,使用依赖注入就少了很有说服力的一个理由。

  那么这里我就介绍一下,怎么样把Dagger2应用到单元测试中。熟悉dagger2的童靴可能知道,Dagger2里面最关键的有两个概念, Module 和 Component 。Module是负责生成诸如DataModel这样被别人(比如DataActivity)使用的类的地方。用术语的话,被别人使用的类 DataModel叫Dependency,使用到了别的类的类DataActivity叫Client。而Component则是供Client使用 Dependency的统一接口。也就是说,DataActivity通过Component,来得到一份DataModel的实例。

原文转自: http://www.infoq.com/cn/articles/mogujie-android-unit-testing