Error injecting a mock dependency in dagger2

时间:2016-04-28 23:07:12

标签: java dagger-2

I'm just playing around trying to get to know Dagger2 a bit better. I found a simple example on the web where a Bar depends on a BarDatabase:

    public class Bar {
       // Bar depends on BarDatabase
       private BarDatabase db;

       @Inject
       public Bar(BarDatabase db) {
          this.db = db;
       }

       public BarDatabase getBar() { return db; }

       public Cocktail getCocktail(String name) {
          String row = db.getCocktail(name);
          return new Cocktail(row);
       }
    }

Dagger works just peachy with the following Module and Component:

    package com.example.eflatt.di.barcomponent;
    import com.example.eflatt.di.Bar;
    import com.example.eflatt.di.module.BarModule;
    import com.example.eflatt.di.testmodule.TestBarModule;
    import javax.inject.Singleton;
    import dagger.Component;

     /**
      * Created by eflatt on 4/26/16.
      */
    @Singleton
    @Component(modules = {BarModule.class, TestBarModule.class})
    public interface BarComponent {
        public Bar provideBar();
    }

    package com.example.eflatt.di.module;

    import com.example.eflatt.di.Bar;
    import com.example.eflatt.di.BarDatabase;
    import com.example.eflatt.di.MockBarDatabase;
    import javax.inject.Singleton;
    import dagger.Module;
    import dagger.Provides;

    /**
     * Created by eflatt on 4/26/16.
     */
    @Module
    public class BarModule {
       @Provides @Singleton
       public Bar provideBar () {
       return new Bar(new BarDatabase());
    }
 }

But I wanted to try injecting a MockBarDatabase() in my unittest, which is when things went off the rails.

Question 1: I decided to write a TestBarModule which new'd a MockBarDatabase() in the provideBar method (separate file called TestBarModule) - is this the wrong technique?

    @Module
    public class TestBarModule {
      @Provides @Singleton
      public Bar provideBar () {
          return new Bar(new MockBarDatabase());
      }
    }

Question 2: If writing a separate Module to handle the Mock case is the right thing to do (I'm guessing it's not), then how do I deal with the fact that my IDE tells me:

Error:(15, 16) error: com.example.eflatt.di.Bar is bound multiple times: @Provides @Singleton com.example.eflatt.di.Bar com.example.eflatt.di.module.BarModule.provideBar() @Provides @Singleton com.example.eflatt.di.Bar com.example.eflatt.di.testmodule.TestBarModule.provideBar()

1 个答案:

答案 0 :(得分:0)

  

这是错误的技术吗?

您所做的是,您将所有内容配置为使用构造函数注入

@Inject
public Bar(BarDatabase db) {
   this.db = db;
}

然后你决定自己提供它,最重要的是不要使用匕首框架并自己处理事情

@Provides @Singleton
public Bar provideBar () {
    // MockBarDatabase isn't supplied by dagger but by you.
    return new Bar(new MockBarDatabase());
}

改进

至少你应该提供自己的依赖。然后,您还将看到为模拟对象切换单个对象更加容易

// this would be a 'righter' way to do it
@Provides @Singleton
public Bar provideBar (MockBarDatabase db) {
    return new Bar(db);
}

MockBarDatabase必须通过上述示例中的其他@Provides方法提供。但如前所述,您已经为构造函数注入配置了所有内容。所以使用它。您可以继续并省略任何new来电:

// dagger will create the object for you!
@Provides @Singleton
public Bar provideBar (Bar bar) {
    return bar;
}

这也是您实现界面的方式。或者只是充分利用构造函数注入,并且不要在模块中指定自己的方法。现在,如果您真的希望自己的酒吧成为单身人士,那么您必须适应您的课程:

@Singleton
public class Bar {
    @Inject Bar(Db db) {/**/}
    // ...
}

是的,你的模块只需提供BarDatabase,如果它也不能被构造函数注入。

如果对象具有带注释的构造函数,并且还可以创建它们的依赖项(构造函数注入)或提供(那些@Provide和{ {1}})。因此,如果您不能使用构造函数注入或需要限定符或其他设置,请使用它,并且仅使用模块。

我不知道您是如何创建此错误的,但我相信您尝试添加第二个模块,将模拟对象提供给同一个组件?

如果你想模拟模块尝试子类化模块,覆盖方法来提供对象。

匕首2上有很多很好的教程和样本,我强烈建议你深入研究它们。