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