Search code examples
androidkotlinjunitkodein

Kodein override binding for AndroidTest


I want to override a Kodein binding by a mock before testing my class.

There is my Kodein init:

val kodein = Kodein {
    bind<MyRepository>() with provider { MyRepository() }
}

Then my class to test:

class MyClass {
    private val mMyRepository: MyRepository by kodein.instance()

    suspend fun sendData() = mMyRepository.sendData()
}

And my test class:

@RunWith(AndroidJUnit4::class)
class MyClassTest {

    @MockK
    lateinit var mMyRepositoryMock: MyRepository

    val mMyClass = MyClass()

    @Before
    fun setUp() {
        MockKAnnotations.init(this, relaxUnitFun = true)
    }

    @Test
    fun testSendData() {
        coEvery { mMyRepositoryMock.sendData() } returns Unit

        runBlocking {
            mMyClass.sendData()
                .collect {
                    assertTrue(true)
                }
        }
    }
}

I want to override mMyRepository value in MyClass during my test by mMyRepositoryMock.

Can somebody help me doing it?


Solution

  • This is precisely why we do not recommend using a global Kodein instance.

    The best way to ensure a class testability is to remove its context dependency.

    Consider the following class:

    class MyClass(override val kodein: Kodein) {
        private val mMyRepository: MyRepository by kodein.instance()
    
        suspend fun sendData() = mMyRepository.sendData()
    }
    

    Now the kodein it uses is passed as parameter, and can therefore be properly configured for tests:

    @RunWith(AndroidJUnit4::class)
    class MyClassTest {
    
        @MockK
        lateinit var mMyRepositoryMock: MyRepository
    
        val kodein by Kodein.lazy {
            bind<MyRepository>() with provider { mMyRepositoryMock }
        }
    
        val mMyClass by lazy { MyClass(kodein) }
    
        @Before
        fun setUp() {
            MockKAnnotations.init(this, relaxUnitFun = true)
        }
    
        @Test
        fun testSendData() {
            coEvery { mMyRepositoryMock.sendData() } returns Unit
    
            runBlocking {
                mMyClass.sendData()
                    .collect {
                        assertTrue(true)
                    }
            }
        }
    }