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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 17:07:02