简体   繁体   English

将 Dagger 与 Espresso 搭配使用

[英]Using Dagger with Espresso

I'm planning to create Espresso tests on my app multi-module, and I'm about to create the first Espresso test, but what I'm seeing is that on my app I do not have an AppComponent where I can fake it.我计划在我的应用程序多模块上创建 Espresso 测试,我即将创建第一个 Espresso 测试,但我看到的是在我的应用程序上我没有可以伪造它的AppComponent Since I want to add the test on my feature-module, I'll create the TestApp , TestRunner there from now.因为我想在我的功能模块上添加测试,所以我将从现在开始在那里创建TestAppTestRunner

What I have on my feature-module is a FeatureComponent that is injected via ComponentFactory from the App , so what I thought is to create a class like this:我的功能模块上有一个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    
}

TEST测试

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 }
}

To better understand the problem为了更好地理解问题

When I run my test and I put a debugger point I see everything is mocked but the Presenter, and that's because the presenter is in当我运行我的测试并放置一个调试器点时,我看到除了演示者之外的所有东西都被模拟了,那是因为演示者在

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

And in my test component I don't have access to "override" this subcomponent so everything is mocked but this subcomponent and I need this mocked.在我的测试组件中,我无权“覆盖”这个子组件,所以一切都被模拟了,但是这个子组件我需要这个模拟。

I don't know if that's the best idea but if I did not misunderstand you you want this Presenter to return a mock {} .我不知道这是否是最好的主意,但如果我没有误解你,你希望这个 Presenter 返回一个mock {} The changes you could do are:你可以做的改变是:

  1. In your TestComponent change interface to abstract class在您的 TestComponent 更改interfaceabstract class
  2. Duplicate your subcomponent and extends from the real one复制您的子组件并从真实的扩展
@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....
       }
    }
}

And that's it, it should work.就是这样,它应该工作。

So as far as i get it, you have multiple modules, components and subcomponents, but since most of their names don't quite match up in the code you posted, nor you posted the error log, i have to guess whats going wrong where.据我所知,您有多个模块、组件和子组件,但是由于它们的大多数名称在您发布的代码中并不完全匹配,也不是您发布的错误日志,我不得不猜测哪里出了问题.

Instead, how about something like this.相反,这样的事情怎么样。

public interface SomeComponent {

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

and then have your production component look something like this:然后让您的生产组件看起来像这样:

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

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

       SomeProductionScopedComponent build();
    }
}

and 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();
    }
}

then, given that you're somewhat manually instantiating the components with these Builders, as long as they depend on the interface ( SomeComponent ), you should be able to bind a ProductionSomeComponent or a TestSomeComponent into your module.然后,假设您在某种程度上使用这些构建器手动实例化组件,只要它们依赖于接口( SomeComponent ),您应该能够将 ProductionSomeComponent 或 TestSomeComponent 绑定到您的模块中。

Makes sense or gives some hint?有道理还是给出了一些提示?

Your example code is pretty complex to understand and the actual problem.您的示例代码很难理解和实际问题。 But what I understand you want to setup expresso test for your feature module and you need to setup dagger component for it.但是我知道你想为你的功能模块设置 expresso 测试,你需要为它设置 dagger 组件。

So, I can give you some guidelines and example code so that you can follow and setup your dagger architecture for your espresso test very simply.因此,我可以为您提供一些指南和示例代码,以便您可以非常简单地为您的 espresso 测试遵循和设置您的匕首架构。

First of all, you need setup/create your App for espresso test like this:首先,您需要为 espresso 测试设置/创建应用程序,如下所示:

class MyTestApplication : MyApplication() {

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

Then create your Test app component like 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
}

Also, make sure to initialize your component in your Test activity class when launching the activity like this:此外,请确保在启动活动时在您的测试活动 class 中初始化您的组件,如下所示:

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

Now you have done all the setup and your dependency should resolve as well as your espresso test should work.现在您已经完成了所有设置,您的依赖项应该可以解决,并且您的 espresso 测试应该可以工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM