Переопределить компонент / модуль Dagger2, созданный с помощью @ Component.Factory - PullRequest
1 голос
/ 11 июня 2019

Я пытаюсь найти правильный способ переопределить Компонент или Модуль в тесте Эспрессо при использовании последних функций Dagger2 @Component.Factory. У меня есть следующее определение компонента:

@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()
}

Некоторые это интерфейс, а SomeImpl будет его реализацией.

Теперь, чтобы внедрить в нашу деятельность, мы просто делаем:

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

Мы разрешаем sessionComponent с помощью функции расширения, объявленной в нашем CustomApp: Application

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

Теперь согласно рекомендациям официального руководства: https://dagger.dev/testing мы должны попытаться заменить MainComponent на TestMainComponent , который будет иметь TestSomeModule с нашей тестовой реализацией.

Мои первые мысли были в этом направлении: Удалите инъекцию из упражнения и замените ее объектом Injector , который будет это делать. Также измените метод create () и передайте модуль :

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)

Идея заключалась в том, что в тесте пользовательского интерфейса мы могли бы сделать:

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

и измените реализацию таким образом. Это не так, как мы думаем, поскольку SomeModule это одноэлементный объект, и мы не можем расширить его.

Это привело меня к следующему подходу: Приложение имеет release и debug buildTypes . В папке release и debug у меня есть реальная реализация Injector 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, однако мне интересно, есть ли лучший способ избежать использования двух Инжектор объектов для этой цели, или, возможно, просто обновить Модуль , используемый с тестовым. Любой совет, это приветствуется!

...