测试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
相关产品推荐
相关产品推荐

