在 Espresso 中使用 Dagger

Using Dagger with Espresso

我计划在我的应用多模块上创建 Espresso 测试,我即将创建第一个 Espresso 测试,但我看到我的应用上没有 AppComponent 我可以在哪里伪造它。因为我想在我的功能模块上添加测试,所以我将从现在开始在那里创建 TestAppTestRunner

我的功能模块上有一个 FeatureComponent,它是通过 ComponentFactoryApp 注入的,所以我想创建一个 class像这样:

@Component (
     dependencies = [ MoreComponents::class],
     modules = [ DataSourceModule::class ]
)
interface FeatureOneComponent { 

    fun activityOneSubComponent(): FeatureOneActivity.Component.Factory
    fun activityTwoSubComponent(): FeatureTwoActivity.Component.Factory

    @Component.Factory
    interface Factory {
        fun create(
            dependencies
        ):FeatureOneComponent
    }
}

interface FeatureOneProvider {
    fun getFeatureOneComponent(): FeatureOneComponent
}


///ACTIVITY

class FeatureOneActivity : AppCompatActivity() {

    //this comes from Subcomponent is what I want to MOCK 
    @Inject lateinit var presenter

    //these comes from the factory and I have it mocked
    @Inject lateinit var manager

    override fun onCreate(){
        (applicationContext as FeatureOneProvider).getFeatureOneComponent().activityOneSubComponent().create(this).inject(this)
    }
}

@Subcomponent(modules = [ActivityOneModule::class]) <--- THIS I WANT TO MOCK
interface Component {
    fun inject(activity: FeatureOneActivity)

    @SubComponent.Factory
    interface Factory {
        fun create(@BindsInstance activity: FeatureOneActivity): Component
    }
}

@Module
interface ActivityOneModule {
    @Binds
    fun bindPresenter(impl: PresenterImpl): Contract.Presenter    
}

测试

class MyTestApp : Application(), FeatureOneProvider {

    override fun getFeatureOneComponent(): FeatureOneComponent {
        return DaggerMockFeatureOneComponent.create()
    }
}

@Component(
    modules = [MockFeatureOneModules::class]
)
interface MockFeatureOneComponent : FeatureOneComponent {
   

    //I NEED TO MOCK THE SUBCOMPONENT WITH `MockSubcomponent`
}

@Component 
object MockFeatureOneModules {

    @Provides
    fun providesManager() : MyManager = mock(MyManager)
}

//I want to use this module to replace the subcomponent of my activity
@Module
object MockSubcomponent() {
  @Provides
  fun providesFakePresenter() : FeatureOneContract.Presenter = mock { FeatureOneContract.Presenter::class.java }
}

更好地理解问题

当我 运行 我的测试和我放置调试器点时,我看到除了演示者之外的所有东西都被模拟了,那是因为演示者在

@Subcomponent(modules = [ActivityOneModule::class]
interface Component {
    fun inject(activity: FeatureOneActivity)

    @SubComponent.Factory
    interface Factory {
        fun create(@BindsInstance activity: FeatureOneActivity): Component
    }
}

@Module
interface ActivityOneModule {
    @Binds
    fun bindPresenter(impl: PresenterImpl): Contract.Presenter    
}

并且在我的测试组件中,我无权“覆盖”这个子组件,所以除了这个子组件之外的所有东西都被模拟了,我需要这个模拟。

据我所知,你有多个模块、组件和子组件,但由于它们的大部分名称与你发布的代码不完全匹配,你也没有发布错误日志,我不得不猜猜哪里出了问题。

相反,像这样的东西怎么样。

public interface SomeComponent {

    WhateverClass1 provideWhatever1();
    WhateverClass2 provideWhatever2();
    WhateverRepository1 provideWhateverRepository1();
    SomeOtherComponent getSomeOtherComponent();
   // and so on and on
}

然后让您的生产组件看起来像这样:

@SomeComponentSingleton
@Component(modules = { ... },
        dependencies = { ... })
public interface SomeProductionScopedComponent extends SomeComponent {

    @Component.Builder
    interface Builder {
       // you know best what needs to go here

       SomeProductionScopedComponent build();
    }
}

还有这个

@SomeComponentSingleton
@Component(dependencies = SomeComponent.class, modules =
        { ... })
public interface TestSomeComponent {

    @Component.Builder
    interface Builder {
        Builder bindCoreComponent(SomeComponent coreComponent);

        @BindsInstance
        Builder bindContext(Context context);

        TestSomeComponent build();
    }
}

然后,假设您使用这些构建器手动实例化组件,只要它们依赖于接口 (SomeComponent),您应该能够将 ProductionSomeComponent 或 TestSomeComponent 绑定到您的模块。

有道理还是给出了一些提示?

您的示例代码理解起来非常复杂,实际问题也很复杂。 但据我所知,您想为您的功能模块设置 expresso 测试,并且您需要为其设置 dagger 组件。

所以,我可以给你一些指导方针和示例代码,这样你就可以非常简单地遵循和设置你的 Dagger 架构来进行 Espresso 测试。

首先,您需要 setup/create 您的应用程序来进行浓缩咖啡测试,如下所示:

class MyTestApplication : MyApplication() {

    //Call this from MyApplication onCreate()
    override fun initDaggerGraph() { 
        component = DaggerTestAppComponent.builder()
            .application(this)
            .appLifecycle(appLifecycle)
            .build()
        component.inject(this)
    }
}

然后像这样创建您的测试应用组件:

//Add all of your dependent modules in this TestAppModule
@Component(modules = [TestAppModule::class])
interface TestAppComponent : AppComponent {

    @Component.Builder
    interface Builder {
        @BindsInstance
        fun application(application: Application): Builder

        @BindsInstance
        fun appLifecycle(appLifecycle: AppLifecycle): Builder

        fun build(): TestAppComponent
    }

    fun inject(activityTest: SomeActivityTest) //Your activity where to inject
}

此外,请确保在启动 activity 时在测试 activity class 中初始化组件,如下所示:

val component = MyApplication.instance.component as TestAppComponent
component.inject(this)

现在您已经完成了所有设置,您的依赖关系应该已经解决,并且您的 espresso 测试应该可以正常工作。

我不知道这是否是最好的主意,但如果我没有误解你的话,你希望这个 Presenter return mock {}。您可以做的更改是:

  1. 在您的 TestComponent 中将 interface 更改为 abstract class
  2. 复制您的子组件并从真实的子组件扩展
@Component(
    modules = [MockFeatureOneModules::class]
)
abstract class MockFeatureOneComponent : FeatureOneComponent {
   

    abstract fun subcomponent() : MockComponent.FactoryMock

    override fun activityOneSubComponent(): FeatureOneActivity.Component.Factory {
        return subcomponent()
    }

    @Subcomponent
    interface MockComponent : FeatureOneActivity.Component { 
       @Subcomponent.Factory
       interface FactoryMock : FeatureOneActivity.Component.Factory {
         override fun create....
       }
    }
}

就是这样,它应该可以工作。