如何模拟未在Module中声明的注入对象?

时间:2016-09-12 12:20:33

标签: java android unit-testing mockito dagger-2

对于dagger2模块

@Module
public class MyModule {
    @Provides @Singleton public RestService provideRestService() {
        return new RestService();
    }

    @Provides @Singleton public MyPrinter provideMyPrinter() {
        return new MyPrinter();
    }
}

我们可以将测试模块作为测试

public class TestModule extends MyModule {
    @Override public MyPrinter provideMyPrinter() {
        return Mockito.mock(MyPrinter.class);
    }

    @Override public RestService provideRestService() {
        return Mockito.mock(RestService.class);
    }
}

但是如果对于下面的类没有在dagger模块中声明...

public class MainService {
    @Inject MyPrinter myPrinter;

    @Inject public MainService(RestService restService) {
        this.restService = restService;
    }
}

如何如上所述创建MainService模拟。

注意,我不打算按照https://medium.com/@fabioCollini/android-testing-using-dagger-2-mockito-and-a-custom-junit-rule-c8487ed01b56#.9aky15kke中的共享对MainService执行测试,而是将我的MainService用于我想测试的另一个普通类。 e.g。

public class MyClassDoingSomething() {
    @Inject MainService mainService;

    public MyClassDoingSomething() {
        //...
    }

    // ...
    public void myPublicFunction() {
        // This function uses mainService
    }
}

1 个答案:

答案 0 :(得分:1)

这绝对不能回答你的问题,但我认为这是相关的,它有用而且对于评论来说太大了。

我经常面对这个问题而且我总是在做#34;构造函数依赖注入"。这意味着我不再通过使用@Inject注释字段来进行字段注入,而是在构造函数中传递依赖项,如下所示:

public class MyClassDoingSomething implements DoSomethig {
    private final  Service mainService;

    @Inject
    public MyClassDoingSomething(Service mainService) {
        this.mainService = mainService;
    }
}

注意构造函数现在如何接收参数并将字段设置为它并且还使用@Inject进行注释?我也想让这些类实现一个接口(也适用于MyService) - 我发现它的其他几个好处使得dagger模块更容易编写:

@Module
public class DoSomethingModule {
   @Provides @Singleton public RestService provideRestService() {
       return new RestService();
   }

   @Provides @Singleton public MyPrinter provideMyPrinter() {
       return new MyPrinter();
   }

   @Provides @Singleton public Service provideMyPrinter(MyService service) {
       return service;
   }

   @Provides @Singleton public DoSomethig provideMyPrinter(MyClassDoingSomething something) {
       return something;
   }
}

(这假定MyService实现或扩展Service

到目前为止,您似乎已经知道匕首能够自己找出依赖图并为您构建所有对象。那么对班级MyClassDoingSomething进行单元测试呢?我甚至不在这里使用匕首。我只是手动提供依赖项:

public class MyClassDoingSomethingTest {
   @Mock
   Service service;

   private MyClassDoingSomething something;

   @Before
   public void setUp() throws Exception {
      MockitoAnnotations.init(this);
      something = new MyClassDoingSomething(service);
   }
   // ...
}

如您所见,依赖关系是手动传递给构造函数的。

显然,如果您编写的某些内容没有可以被您调用的构造函数,那么这不起作用。经典的例子是android活动,片段或视图。有办法实现这一目标,但我个人仍然认为你可以在没有匕首的情况下以某种方式克服这个问题。如果您对具有字段@Inject MyPresenter myPresenter的视图进行单元测试,通常此字段将具有在测试中正常工作的包访问权限:

public class MyViewTest {
   @Mock MyPresenter presenter;

   private MyView view;

   @Before
   public void setUp() throws Exception {
      MockitoAnnotations.init(this);
      view.myPresenter = presenter;
   }
}

请注意,这仅在MyViewTestMyView位于同一个包中时才有效(在android项目中通常就是这种情况)。

如果您仍想使用匕首进行测试,那么在一天结束时,您始终可以创建"测试"可以通过在组件中声明方法来注入的模块和组件,如:

@Inject
public interface MyTestComponent {
   void inject(MyClassDoingSomething something);
}

我发现这种方法很好,但在整个开发过程中,我更喜欢第一种方法。这也报告了Robolectric的问题,build.gradle文件中的某些设置需要实际使匕首编译器运行以进行测试,以便实际生成类。