alwaystest / Blog

24 stars 2 forks source link

使用Dagger2进行依赖注入 #18

Open alwaystest opened 8 years ago

alwaystest commented 8 years ago

使用Dagger2进行依赖注入

标签(空格分隔): Android 测试 Dagger2


废话· 前言

前段时间在尝试使用MVVM模式来编写程序,我负责写ViewModel部分,于是就想搞一搞Android的自动化测试,本来是想直接连带网络请求一并测掉,后来才发现一般测试只负责程序的逻辑,是脱离网络的。而且现在的代码使用的是Volley来处理网络请求的,因为异步所以不方便测网络请求部分。那就测测逻辑吧。

首先我必须承认Dagger2的学习是有一定难度的。首先把google出来的关于Dagger2的文章,连同Android单元测试的文章都看了一遍。然后才开始能一点一点使用起来,但还是处于模糊状态。这里默认读者已经可以初步使用起来Dagger2了(就是 可以使用@Module,@Provides,@Component,@Inject这么几个注解),就记录一下我的理解,有错误的地方还请大家指出。

我看过的文章列表:

  1. https://github.com/frogermcs/GithubClient frogermcs写的一系列文章,教怎么使用Dagger2
  2. http://www.jianshu.com/p/65737ac39c44 牛晓伟写的一系列文章,帮助理解Dagger2是干嘛的,Dagger2的工作方式。
  3. http://chriszou.com/ 小创写的一系列Android单元测试的文章,第六篇重点介绍Dagger2的用法。

    正文

反正Dagger2就是一个高级一些的工厂模式,帮助程序员从繁琐的new方式新建对象中解脱出来,自动解决依赖关系,更改工厂的实例化方法就能全局生效,很方便。

由于Android单元测试的时候一般使用Mockito,需要把被调用的模块替换成Mock出来的对象,如果使用传统的方式在代码里面直接new是 不能测试的,这个时候一个简单的解决办法就把被调用的模块对象通过函数参数的方式传递到函数体内,这样在运行环境传入正常对象,在测试环境传入Mock出来的对象就可以达到方便测试的目的。这就用到了Dagger2的Constructor Injection。Field Injection也可以啦。

Component类里面是可以通过

DaggerXXXComponent.builder().YYYModule(new YYYModule(zzz)).build();

来手动指定Component使用的Module对象的。新建Module对象的时候可以传参,当然此时Module类需要有对应的构造函数,和普通Java 类是一样的。这样就出来一种办法,就是在这个Module类上做文章。在测试包下面新建一个类,继承自原本要传的Module,然后在里面覆盖父类的Provide方法,生产Mock出来的对象,使用这个TestModule来创建Component,然后使用这个Component来生成所需要的对象。现在只要想办法让被测试的函数使用这个Component就可以了。比如提供一个setComponent()。但是这样写的话有点麻烦,为了测试 添加一个set方法,还要写Module的子类,代码不够干净整洁。

于是有这样一种方法,使用Mockito的特性,直接spy掉Module类,Mockito.when().thenReturn();这里把Module产生的对象替换成Mock出来的对象,这样就省掉继承Module写那么一堆东西了,但是还是得想办法让被测试的函数使用这个Component。

在写ViewModel部分的时候,这里基本上是纯Java代码,单元测试倒是没有必要用到Dagger2了,正式环境中使用 Constructor Injection,测试的时候直接把Mock出来的对象作为参数传进被调的函数里就可以了。

Component在实例化依赖对象的时候,首先查找Module里面有没有合适的方法提供 所需的对象,如果没有找到,则会去找@Inject注解过的构造函数。所以需要一个使用了Constructor Inject的类时,在 能提供这个类的依赖对象的 Component类中加一个方法来提供这个类对应的对象。比如这样(代码复制自小创):

@Component(modules = {AppModule.class})
public interface AppComponent {
    LoginPresenter loginPresenter();
}

在需要这个对象的地方执行appComponent.loginPresenter();Dagger2会先找Module,再找@Inject注解过的构造函数,自动解决依赖。反正是不用再new啦。

alwaystest commented 8 years ago

在使用MVP模式来构建APP的时候,初始化Presenter的时候,为了方便测试,需要Mock对应的View,但是View是Activity的话,不方便使用Module来生产对象。

如果有更方便的办法,欢迎指教。