如何使用回调对挂起函数进行单元测试

How to unit test suspend function with callback

我有以下代码:

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)
            )
        }
    }

Sdk为第三方库。我正在使用 suspendCoroutine 暂停协程并在 sdk 完成初始化时恢复。 一切正常,但是当我尝试编写这样的单元测试时,我得到以下 IllegalStateException: This job has not completed yet。我正在使用 mockito-kotlin 编写以下测试:

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

        initializeSdk(sdk)

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

基本上我想做的是能够测试 resumeresumeWithException

就我个人而言,我不喜欢模拟。如果 Sdk 是一个接口,您可以只提供您自己的测试实现来执行您的测试(成功和错误结果)。您可以精确控制 when/if 调用回调等

如果 Sdk 是您无法控制的 class,您可以创建一个接口来抽象 Sdk class,并使您的 initialize 方法请改用您自己的界面。但是我不得不承认这并不理想。

如果您坚持使用模拟,通常模拟库可以让您使用调用参数来模拟对方法调用的响应。使用 Mockito,它应该是这样的:

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()
    }
}

(虽然我不是 Mockito 的专家,所以可能会有更简洁或类型安全的方法 :D)