簡體   English   中英

將 Dagger 與 Espresso 搭配使用

[英]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    
}

在我的測試組件中,我無權“覆蓋”這個子組件,所以一切都被模擬了,但是這個子組件我需要這個模擬。

我不知道這是否是最好的主意,但如果我沒有誤解你,你希望這個 Presenter 返回一個mock {} 你可以做的改變是:

  1. 在您的 TestComponent 更改interfaceabstract 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....
       }
    }
}

就是這樣,它應該工作。

據我所知,您有多個模塊、組件和子組件,但是由於它們的大多數名稱在您發布的代碼中並不完全匹配,也不是您發布的錯誤日志,我不得不猜測哪里出了問題.

相反,這樣的事情怎么樣。

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 組件。

因此,我可以為您提供一些指南和示例代碼,以便您可以非常簡單地為您的 espresso 測試遵循和設置您的匕首架構。

首先,您需要為 espresso 測試設置/創建應用程序,如下所示:

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
}

此外,請確保在啟動活動時在您的測試活動 class 中初始化您的組件,如下所示:

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

現在您已經完成了所有設置,您的依賴項應該可以解決,並且您的 espresso 測試應該可以工作。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM