Search code examples
androiddagger-2daggerdaggermock

How to inject a MockModule into DaggerAppComponent?


I have been using Dagger Android for a while, and now I want to inject a MockModule for testing purposes, and I do not know how to get it. I have seen on some posts that they call something like:

DaggerAppComponent.builder()
  .dataModule(myMockModule)
  .create(this).inject(this)

But I have this configuration:

@Singleton
@Component(modules = [
    AndroidSupportInjectionModule::class,
    ActivitiesBuilderModule::class,
    AppModule::class,
    DataModule::class
])
internal interface AppComponent : AndroidInjector<CbvApplication> {

    @Component.Builder
    abstract class Builder : AndroidInjector.Builder<CbvApplication>()

}

and when I create the application, the DaggerAppComponent.Builder does not provides the setters for the individual modules. That is nice cause in the app code I do not have to worry about it, but on testing, I want to inject my mock objects through mockComponents / modules.

Anyone can point me in the right direction???


Solution

  • As far as I know, I am not sure we can mock the whole module all together, but we can mock dependencies provided by module. I have achieved this like, I need to mock DataModule for UI Testing so I have created this TestDataModule and provided mocks for the dependency.

    import com.nhaarman.mockito_kotlin.mock
    import dagger.Module
    import dagger.Provides
    import javax.inject.Singleton
    
    @Module
    class TestDataModule {
    
        @Provides
        @Singleton
        fun providesDataRepository(): DataRepository {
            return mock()
        }
    }
    

    Here is TestApplicationModule

    @Module
    abstract class TestApplicationModule {
    
        @Binds
        abstract fun bindContext(application: Application): Context
    }
    

    And created TestApplicationComponent which will take the required modules

    @Singleton
    @Component(modules = [
        (AndroidSupportInjectionModule::class),
        (TestApplicationModule::class),
        (UIModule::class),
        (PresentationModule::class),
        (TestDataModule::class)])
    interface TestApplicationComponent {
    
        // Here you can add additional direct mock 
        // classes to access them directly from component reference
        fun dataRepository(): DataRepository 
    
    
        @Component.Builder
        interface Builder {
            @BindsInstance
            fun application(application: Application): TestApplicationComponent.Builder
    
            fun build(): TestApplicationComponent
        }
    
        fun inject(app: TestAppplication)
    }
    

    I had to test UI using Android JUnit Test runner and espresso, So I used UIModule and Presentation Module as it is (Cannot mock as wanted to test). So have to mock other dependencies which are not part of that UI Unit tests like DataRepository

    You can add other modules like CacheModule and RemoteModules as mocks as they don't play any role in UI Unit testing.

    Finally, Create DaggerTestApplicationComponent in TestApplication like this,

    class TestApplication : Application(), HasActivityInjector {
    
        @Inject lateinit var activityInjector: DispatchingAndroidInjector<Activity>
        private lateinit var appComponent: TestApplicationComponent
    
        override fun onCreate() {
            super.onCreate()
            appComponent = DaggerTestApplicationComponent.builder()
                    .application(this)
                    .build()
            appComponent.inject(this)
        }
    
        override fun activityInjector(): AndroidInjector<Activity> = activityInjector
    }
    

    So now DaggerTestApplicationComponent will generate required dependency graph.

    Note: It is required to stub all the methods called on mocked dependencies