使用Turbine测试Kotlin Flow未发射Loading状态的问题求助
问题:Turbine测试Kotlin Flow时未捕获Loading状态
我是单元测试新手,尝试用Turbine库测试Kotlin Flow,但发现Flow没有发射所有值。测试始终失败,报错如下:
Expected UiState$Loading
Actual :Content
测试代码
fun `send function should emit Loading and Content states`() = runTest { // Arrange val userProfile = UserProfile(login = "test_login") val contentState = UiState.Content(userProfile) coEvery { fakeRepository.getDetail(any()) } returns userProfile // Act viewModel.send(userProfile.login!!) // Assert testScheduler.advanceUntilIdle() viewModel.uiState.test { assertEquals(UiState.Loading, awaitItem()) assertEquals(contentState, awaitItem()) cancelAndIgnoreRemainingEvents() } }
ViewModel代码片段
viewModelScope.launch(dispatchers.main) { flow { emit(UiState.Loading) val mResponse = userRepository.getDetail(login = login) emit(UiState.Content(mResponse)) }.catch { UiState.Error(it.message.toString()) it.printStackTrace() }.flowOn(dispatchers.main).collect { _uiState.value = it } }
解决方案
1. 核心问题:测试时机错误
你当前的测试流程是先调用send()、推进调度器,再启动Turbine监听。但_uiState是StateFlow,它只保留最新值。当advanceUntilIdle()执行完,Loading状态已经被后续的Content覆盖,所以监听时只能拿到Content,自然断言失败。
2. 修正测试代码
应该先启动Turbine监听,再执行send()操作,最后推进调度器:
fun `send function should emit Loading and Content states`() = runTest { // Arrange val userProfile = UserProfile(login = "test_login") val contentState = UiState.Content(userProfile) coEvery { fakeRepository.getDetail(any()) } returns userProfile // 先启动监听,再执行操作 viewModel.uiState.test { // Act viewModel.send(userProfile.login!!) testScheduler.advanceUntilIdle() // Assert assertEquals(UiState.Loading, awaitItem()) assertEquals(contentState, awaitItem()) cancelAndIgnoreRemainingEvents() } }
3. 额外修复ViewModel的问题
你的catch块只是创建了UiState.Error但没有发射出去,导致错误状态无法被收集:
viewModelScope.launch(dispatchers.main) { flow { emit(UiState.Loading) val mResponse = userRepository.getDetail(login = login) emit(UiState.Content(mResponse)) }.catch { emit(UiState.Error(it.message ?: "Unknown error")) // 改为emit发射错误状态 it.printStackTrace() }.collect { _uiState.value = it } }
另外flowOn(dispatchers.main)可以去掉,因为协程已经在main上下文启动,挂起函数getDetail的调度会由自身处理。
内容的提问来源于stack exchange,提问作者Jitendra Prajapati
相关产品推荐
相关产品推荐

