How to verify method calls in different CoroutineScope in unit tests with Mockk?

88 Views Asked by At

I am trying to verify that method in dependency object is called, but since it happens in different CoroutineScope coVerify always fails.

How to verify that someMethod() is called?

Here is my code:

SUT:

class SomeClass(
    private val coroutineScope: CoroutineScope,
    private val someDependency: SomeDependency
) {
    fun run() {
        coroutineScope.launch {
            someDependency.someMethod()
        }
    }
}

Dependency:

class SomeDependency {
    fun someMethod() {
        println("SomeDependency.someMethod")
    }
}

Test(always fails):

class SomeClassTest {


    @MockK(relaxed = true)
    private lateinit var someDependency: SomeDependency

    private val testScope = TestScope()
    private lateinit var someClass: SomeClass

    @Before
    fun setup() {
        MockKAnnotations.init(this)
        someClass = SomeClass(
            testScope,
            someDataSource
        )
    }

    @Test
    fun `when run, should call someDependency someMethod`() = runTest {
        someClass.run()
        coVerify { someDependency.someMethod() }
    }

}
2

There are 2 best solutions below

0
Oleh Liskovych On

Found how to use same scope for SUT class and test itself:

class SomeClassTest {

    private var someDependency: SomeDependency = mockk(relaxed = true)

    @Test
    fun `when run, should call someDependency someMethod`() = runTest {
        val someClass = SomeClass(
            this,
            someDependency
        )
        someClass.run()
        advanceUntilIdle()
        verify { someDependency.someMethod() }
    }

}
0
Oleh Liskovych On

Another solution is to use test coroutine rule

Rule:

class TestCoroutineRule(
    val testCoroutineDispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher(),
    val testCoroutineScope: TestCoroutineScope = TestCoroutineScope(testCoroutineDispatcher)
) : TestWatcher() {

    override fun starting(description: Description) {
        super.starting(description)
        Dispatchers.setMain(testCoroutineDispatcher)
    }

    override fun finished(description: Description) {
        super.finished(description)
        testCoroutineDispatcher.cleanupTestCoroutines()
        Dispatchers.resetMain()
    }
}

Test:

@OptIn(ExperimentalCoroutinesApi::class)
class SomeClassTest2 {

    @get:Rule
    val testCoroutineRule = TestCoroutineRule()

    private var someDependency: SomeDependency = mockk(relaxed = true)
    private lateinit var someClass: SomeClass

    @Before
    fun setUp() {
        someDependency = mockk(relaxed = true)
        someClass = SomeClass(testCoroutineRule.testCoroutineScope, someDependency)
    }

    @Test
    fun `when run, should call someDependency someMethod`() = runTest {
        someClass.run()
        advanceUntilIdle()
        verify { someDependency.someMethod() }
    }

}