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

测试Jetpack Compose+Ktor代码时协程无法正常推进的问题

协程推进异常问题解决方案

问题核心

你遇到的问题本质是:ViewModel调用链中,Ktor网络请求使用的默认Dispatchers.IO未被纳入测试用TestDispatcher的管控范围,导致单次advanceUntilIdle()只能推进Main调度器上的任务,无法触发IO线程中Ktor请求后续的代码执行。而直接调用仓库方法时,协程是在runTest的TestScope中执行,自然能被正常推进。

解决方案

方案1:扩展测试规则,替换IO调度器

Ktor默认使用Dispatchers.IO处理网络请求,你的MainDispatcherRule仅替换了Main调度器,导致IO线程的任务不受测试调度器控制。修改规则,把IO调度器也替换为TestDispatcher:

@OptIn(ExperimentalCoroutinesApi::class)
class MainDispatcherRule(
    val testDispatcher: TestDispatcher = StandardTestDispatcher()
) : TestWatcher() {
    override fun starting(description: Description) {
        Dispatchers.setMain(testDispatcher)
        // 将IO调度器替换为测试用调度器
        Dispatchers.setDispatcher(Dispatchers.IO, testDispatcher)
    }

    override fun finished(description: Description) {
        Dispatchers.resetMain()
        Dispatchers.resetDispatcher(Dispatchers.IO)
    }
}

方案2:给Ktor HttpClient指定测试调度器

在创建Ktor的HttpClient时,明确指定使用测试调度器,确保网络请求全程在TestDispatcher中执行:

首先修改WeatherRepository,支持注入调度器:

class WeatherRepository(
    engine: HttpClientEngine,
    private val locationClient: LocationClient,
    // 允许注入自定义调度器,默认用IO
    private val dispatcher: CoroutineDispatcher = Dispatchers.IO
) {
    private val httpClient = HttpClient(engine) {
        // 你的其他配置(比如ContentNegotiation等)
        engine {
            // 以CIO引擎为例,设置调度器
            if (this is CIOEngineConfig) {
                this.dispatcher = dispatcher
            }
            // 其他引擎类似,比如OkHttpEngineConfig也有dispatcher配置项
        }
    }

    // ... 原有代码
}

然后在测试的HomeBaseRule中,初始化WeatherRepository时传入测试调度器:

// 在HomeBaseRule的初始化逻辑中
val weatherRepository = WeatherRepository(
    engine = yourEngine,
    locationClient = mockLocationClient,
    dispatcher = dispatcherRule.testDispatcher
)

验证调整后的测试

修改完成后,单次advanceUntilIdle()就能推进所有协程任务,包括Ktor请求后的代码执行:

@Test
fun fetchCurrentWeather_success() = runTest(dispatcherRule.testDispatcher) {
    homeBaseRule.viewModel.handleIntent(HomeIntent.GetCurrentWeather)
    advanceUntilIdle()
    // 这里可以正常断言UI状态更新
    assertNotNull(homeBaseRule.viewModel.uiState.value.currentWeather)
}

内容的提问来源于stack exchange,提问作者Yauheni Mokich

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 19:53:10