You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何对带回调的Kotlin suspend挂起函数进行单元测试

问题原因

你遇到的报错是因为initialize方法内的suspendCoroutine始终未被恢复:你仅mock了Sdk实例,但没有触发sdk.initialize传入的callback/onFailure回调,协程一直处于挂起状态,runBlockingTest等待任务完成超时就抛出了该异常。

测试实现方案

我们可以通过Mockito的参数捕获器(ArgumentCaptor)拿到sdk.initialize传入的两个回调参数,手动触发回调来模拟成功、失败两种场景,同时也可以覆盖sdk方法直接崩溃的场景。

以下是三种场景的测试代码,如果你用的是旧版协程测试库,把代码中的runTest替换为runBlockingTest即可:

1. 初始化成功场景测试

@Test
fun `initialize should resume successfully when sdk callback is triggered`() = runTest {
    val sdk = mock<Sdk>()
    // 定义参数捕获器,捕获sdk.initialize传入的两个回调
    val callbackCaptor = argumentCaptor<() -> Unit>()
    val failureCaptor = argumentCaptor<(Throwable) -> Unit>()

    // 启动initialize方法,此时协程挂起
    val initializeJob = async { initialize(sdk) }
    
    // 验证sdk.initialize被调用,同时拿到两个回调参数
    verify(sdk).initialize(callbackCaptor.capture(), failureCaptor.capture())
    
    // 手动触发成功回调,恢复协程
    callbackCaptor.firstValue.invoke()
    
    // 等待协程执行完成,无异常抛出即为测试通过
    initializeJob.await()
}

2. 初始化失败回调触发场景测试

@Test
fun `initialize should throw SdkException when onFailure callback is triggered`() = runTest {
    val sdk = mock<Sdk>()
    val callbackCaptor = argumentCaptor<() -> Unit>()
    val failureCaptor = argumentCaptor<(Throwable) -> Unit>()
    val testError = RuntimeException("Sdk init failed")

    val initializeJob = async { initialize(sdk) }
    verify(sdk).initialize(callbackCaptor.capture(), failureCaptor.capture())
    
    // 手动触发失败回调
    failureCaptor.firstValue.invoke(testError)
    
    // 验证抛出的异常符合预期
    val exception = shouldThrow<SdkException> {
        initializeJob.await()
    }
    assert(exception.message == testError.message)
}

3. Sdk.initialize直接抛出异常场景测试

@Test
fun `initialize should throw SdkException when sdk initialize crashes directly`() = runTest {
    val sdk = mock<Sdk>()
    val testCrash = RuntimeException("Inner sdk crash")
    // mock sdk.initialize被调用时直接抛出异常
    whenever(sdk.initialize(any(), any())).thenThrow(testCrash)

    // 验证抛出的异常符合预期
    val exception = shouldThrow<SdkException> {
        initialize(sdk)
    }
    assert(exception.message == "Crash inside SDK")
    assert(exception.cause == testCrash)
}

内容的提问来源于stack exchange,提问作者Henrique Rocha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 22:42:01