A key strength of Dependency Injection is that unit testing with DI supposed to be easy by switching components with their mock representatives.

To be able to successfully mock a dependency is not hard, but it still required a few steps that may take a couple hours to get right. Below is a tutorial on how to mock Dependency Injected classes.

Assuming you already have DI up and running in your main project, you will have a similar class:

//AppModule.kt
@Module
class AppModule (...) {
@Provides
@Singleton
fun provideSomething() = Something()
...
}

//AppComponent.kt
@Singleton
@Component(modules = [AppModule::class])
interface AppComponent {
    fun inject(target: SomeClass)
...
}

//YourApplication.kt
...
override fun onCreate() {
    super.onCreate()
    appComponent = DaggerAppComponent.builder()
                      .appModule(AppModule())
                      .build()
...

In your test source (whether it is unit tests or instrumented tests), create a mock module class:

//MockAppModule.kt
@Module
class MockAppModule (...) {
    @Provides
    @Singleton
    fun provideSomething(): Something = mockk(relaxed = true) 
//This example use mockk, but you can replace it with any
//other mocking library, or even a mock subclass.
...
}

and the mock components class:

//MockComponent
@Singleton
@Component(modules = [TestAppModule::class])
interface MockComponent : AppComponent {
    fun getSomething(): Something 
//This declaration is only required to access mocked 
//component from inside your test.
... 
}

Now, to the main course, we will need to set up a test rule that would initiate your application class, and then switch the real components with the mocked ones:

class MockComponentRule() : TestRule {
    private val mMockComponent: MockComponent

    override fun apply(base: Statement, description: Description?): Statement {
        return object : Statement() {
            @Throws(Throwable::class)
            override fun evaluate() {
                val application: YourApplication = context.applicationContext as YourApplication
                // Set the MockComponent before the test runs
                application.appComponent = mMockComponent
                // Run tests 
                base.evaluate()
            }
        }
    }

    init {
        val application = context.applicationContext as YourApplication
        mMockComponent = DaggerMockComponent.builder()
                .mockAppModule(MockAppModule())
                .build()
    }
    //And finally, to enable test to access mocked component
    fun getMockSomething() = mMockComponent.getSomething()
}

Inside one of your test, declare the test rule:

class SomeFragmentTest {
    private val component = MockComponentRule(
InstrumentationRegistry.getInstrumentation().targetContext)
    private val main: ActivityTestRule<MainActivity> =
    ActivityTestRule(MainActivity::class.java,
    false, false)

    @Rule
    @JvmField
    var chain: TestRule = RuleChain.outerRule(component).around(main)
...

Enjoy testing.

m.quan.lam Uncategorized