How to unit test suspend function with callback

Viewed 455

I have the following code:

suspend fun initialize(sdk: Sdk) =
    suspendCoroutine<Unit> { continuation ->
        try {
            sdk.initialize(
                callback = { continuation.resume(Unit) },
                onFailure = { error -> continuation.resumeWithException(SdkException(error.message)) })
        } catch (exception: Exception) {
            continuation.resumeWithException(
                SdkException("Crash inside SDK", exception)
            )
        }
    }

The Sdk is a third-party library. I'm using suspendCoroutine to suspend the coroutine and resume when the sdk finishes initializing. Everything works fine but when I try to write a unit test like this I get the following IllegalStateException: This job has not completed yet. I'm using mockito-kotlin to write the following test:

    @Test
    fun `should initialize sdk correctly`() = runBlockingTest {
        val sdk = mock<Sdk>()

        initializeSdk(sdk)

        verify(sdk).initialize(any(), any())
    }

Basically what I want to do is to be able to test the resume and resumeWithException

1 Answers

Personally I'm not a fan of mocks. If Sdk is an interface, you could just provide your own test implementation to perform your tests (for success and error results). You can control exactly when/if the callback is called etc.

If Sdk is a class that you can't control, you could create an interface to abstract the Sdk class away, and make your initialize method use your own interface instead. However I have to admit this is not ideal.

If you stick with mocking, usually mocking libraries have a way for you to use invocation arguments to mock responses to method calls. With Mockito, it should be something like:

val sdk = mock<Sdk> {
    on { initialize(any(), any()) } doAnswer { invocation ->
        @Suppress("UNCHECKED_CAST")
        val successCallback = invocation.arguments[0] as (() -> Unit) // use callback's function type here
        successCallback.invoke()
    }
}

(although I'm not an expert in Mockito, so there may be more concise or type-safe ways :D)

Related