mockito / mockito-kotlin

Using Mockito with Kotlin
MIT License
3.11k stars 201 forks source link

Mocking suspend function literal #338

Closed pawegio closed 5 years ago

pawegio commented 5 years ago

I want to mock suspend function literal as follows:

val function = mock<suspend () -> Unit> {
    onBlocking { invoke() } doReturn Unit
}

Then verify suspending behavior:

verifyBlocking(function) { invoke() }

However, this verification fails. Is mocking suspend function literals already supported or I need to declare and mock an interface with suspend function as a member?

I'm using mockito-kotlin in version 2.0.0.

bohsen commented 5 years ago

@pawegio

val function = mock<suspend () -> Unit> {
    onBlocking { invoke() } doReturn Unit
}

@Test
fun verifySuspendLiteral() = runBlocking {
    function()
    verify(function).invoke()
}

or


@Test
fun verifySuspendLiteral() = runBlocking {
    val function = mock<suspend () -> Unit> {
        onBlocking { invoke() } doReturn Unit
    }
    function()
    verify(function).invoke()
}
pawegio commented 5 years ago

Thanks @bohsen

It turned out to be an issue related to CoroutineContext in kotlintest framework. Anyway, I fixed it so I'm closing the issue since verification works fine.

mkotyk commented 4 years ago

I'm seeing an issue when the mocked suspend function has arguments.

  @Test
    fun verifySuspendLiteral() = runBlocking {
        val function = mock<suspend (Int) -> Unit> {
            onBlocking { invoke(any()) } doReturn Unit
        }
        function(5)
        verify(function).invoke(argForWhich { this == 5 })
    }

org.mockito.exceptions.misusing.InvalidUseOfMatchersException: Invalid use of argument matchers! 2 matchers expected, 1 recorded:

com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0

pawegio commented 4 years ago

@mkotyk in suspend fun there is a hidden Continuation argument.

Declare an interface:

interface SuspendFunction<I, O> {
    suspend fun invokeSuspend(i: I): O
}

and in your case:

@Test
fun verifySuspendLiteral() = runBlocking {
    val function = mock<SuspendFunction<Int, Unit>> {
        onBlocking { invokeSuspend(any()) } doReturn Unit
    }
    function.invokeSuspend(5)
    verify(function).invokeSuspend(argForWhich { this == 5 })
}