Android 用匕首配浓咖啡

Android 用匕首配浓咖啡,android,kotlin,android-espresso,dagger-2,dagger,Android,Kotlin,Android Espresso,Dagger 2,Dagger,我计划在我的应用程序多模块上创建浓缩咖啡测试,我将创建第一个浓缩咖啡测试,但我看到的是,在我的应用程序上,我没有可以伪造的AppComponent。因为我想在我的功能模块上添加测试,所以我将从现在开始在那里创建TestApp,TestRunner 我的功能模块上有一个FeatureComponent,它是通过ComponentFactory从App注入的,所以我想创建这样一个类: @Component ( dependencies = [ MoreComponents::class],

我计划在我的应用程序多模块上创建浓缩咖啡测试,我将创建第一个浓缩咖啡测试,但我看到的是,在我的应用程序上,我没有可以伪造的
AppComponent
。因为我想在我的功能模块上添加测试,所以我将从现在开始在那里创建
TestApp
TestRunner

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

@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    
}
为了更好地理解这个问题 当我运行我的测试并设置一个调试器点时,我看到除了演示者之外,所有的东西都被模拟了,这是因为演示者处于

@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();
    }
}
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
}
val component = MyApplication.instance.component as TestAppComponent
component.inject(this)
还有这个

@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架构为你的浓缩咖啡测试做准备

首先,您需要按照以下方式设置/创建用于浓咖啡测试的应用程序:

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

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

       SomeProductionScopedComponent build();
    }
}
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
}
val component = MyApplication.instance.component as TestAppComponent
component.inject(this)
然后创建测试应用程序组件,如下所示:

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

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

       SomeProductionScopedComponent build();
    }
}
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
}
val component = MyApplication.instance.component as TestAppComponent
component.inject(this)
此外,在启动活动时,请确保在测试活动类中初始化组件,如下所示:

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

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

       SomeProductionScopedComponent build();
    }
}
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
}
val component = MyApplication.instance.component as TestAppComponent
component.inject(this)

现在您已经完成了所有的设置,您的依赖关系应该得到解决,您的浓缩咖啡测试也应该能够正常工作。

我不知道这是否是最好的主意,但如果我没有误解您的意思,您希望此演示者返回一个
模拟{}
。您可以做的更改包括:

  • 在TestComponent中,将
    接口
    更改为
    抽象类
  • 复制子组件并从真实组件扩展

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

    您不能覆盖组件。您需要在测试中使用“测试组件”,在生产代码中使用“真实组件”。然而,从你发布的代码来看,很难说你在匕首设置中哪里出了问题。是的,但是如果我的子组件有一个
    modules=[…]
    如何伪造这些?让我用clear编辑我的问题code@Shark编辑我的问题,问题是如何“覆盖”我的生产子组件上的模块,并使用我的模拟子组件上的模拟模块。Well,您的生产子组件不使用硬编码模块列表吗?让我试着发布一些东西,看看你是否捕捉到了理想。你是否将
    FeatureOneComponent
    绑定到ApplicationComponent?你确定它绑定的是TestF1Component而不是ProductionF1Component吗?@StuartDTO基本上,TestComponent将包含模块,这些模块将提供(某些)生产对等组件的模拟或测试变体,这只是显示了你应该走的大致方向。这不是一个分步指南或问题的解决方案,只是演示了如何绕过当前存在的问题-绑定生产模块而不是组件中的测试模块。让我用匹配的代码更新问题。您能重新检查问题吗?我编辑了它,它编译了,它工作了,但是我缺少了模拟子组件模块的部分…这是另一种方式,我不想在我的测试中注入,我正在尝试创建一个TestSubcomponent,检查我的问题
    @subcomponent(modules=[ActivityOneModule::class]
    我想模拟这个模块,但我不能模拟这个子组件,其他东西都被模拟了。