首页 > 解决方案 > 覆盖使用 @Component.Factory 创建的 Dagger2 组件/模块

问题描述

我正在尝试在使用最新的 Dagger2 @Component.Factory功能时找到在 Espresso 测试中覆盖组件模块的正确方法。我有以下组件定义:

@Component(
    modules = [SomeModule::class],
    dependencies = [SessionComponent::class]
)
interface MainComponent {
    @Component.Factory
    interface Factory {
        fun create(sessionComponent: SessionComponent): MainComponent
    }
}

SomeModule定义它是:

@Module
object SomeModule {

    @Provides
    @JvmStatic
    fun some(): Some = SomeImpl()
}

Some它是一个接口,SomeImpl将是它的实现。

现在,要注入我们的活动,我们只需:

DaggerMainComponent.factory().create(sessionComponent).inject(this)

我们使用 CustomApp中声明的扩展函数解析sessionComponent :Application

val Context.sessionComponent: SessionComponent
    get() = App.sessionComponent(this)

现在按照官方指南中的建议:https ://dagger.dev/testing我们应该尝试将MainComponent替换为TestMainComponent,它将在我们的测试实现中包含一个TestSomeModule 。

我最初的想法是朝着这个方向:从 Activity 中删除注入,并用一个可以执行此操作的Injector对象替换它。另外,修改create()方法并传递Module

DaggerMainComponent.factory().create(sessionComponent).inject(this)

它已移至:

object Injector {
   @VisibleForTesting
   var someModule: SomeModule = SomeModule()

    fun inject(activity: Activity) {
        when (activity) {
            is MainActivity -> {
                DaggerMainComponent.factory().create(App.sessionComponent(activity), someModule).inject(activity)
            }
        }
    }
}

以及新的工厂定义:

@Component.Factory
 interface Factory {
  fun create(sessionComponent: SessionComponent, someModule: SomeModule): MainComponent
 }

然后,在我们刚刚调用的活动中的onCreate()之后Injector.inject(this)

这背后的想法是,在 UI 测试中我们可以这样做:

@Before
 fun setup() {
  Injector.someModule = TestSomeModule
 }

并以这种方式更改实现。它失败了,因为SomeModule它是一个单例对象,我们不能从它扩展。

这导致我采用以下方法:该应用程序具有发布调试 buildTypes。在发布和调试文件夹中,我有注入器的真正实现 MainComponent引用SomeModule 。然后在androidTest文件夹中,我有另一个Injector对象,但具有以下内容:

object Injector {

    fun inject(activity: Activity) {
        when (activity) {
            is MainActivity -> {
                DaggerTestMainComponent.factory().create(App.sessionComponent(activity)).inject(activity)
            }
        }
    }
}

TestMainComponent它是

@Component(
    modules = [TestSomeModule::class],
    dependencies = [SessionComponent::class]
)
interface TestMainComponent {
    @Component.Factory
    interface Factory {
        fun create(sessionComponent: SessionComponent): TestMainComponent
    }
}

@Module
object TestSomeModule {
    @Provides
    @JvmStatic
    fun greeter(): Some = TestSomeImpl()
}

这种方法有效,gradle 将调试文件夹中的Injector对象替换为 androidTest 文件夹中的对象,但是,我想知道是否有更好的方法来避免为此目的使用两个Injector对象,或者只是更新模块与测试一一起使用。欢迎任何建议!

标签: androidandroid-espressodagger-2

解决方案


推荐阅读