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

Android单元测试emitAll返回的Flow报NoSuchElementException如何解决

问题背景

现有通过emitAll返回Flow的业务方法实现如下:

fun handle(actions: MoviesActions): Flow<MoviesStates> = flow {
    when (actions) {
        is MoviesActions.LoadMovies -> {
            emit(MoviesStates.Loading)
            emitAll(moviesUseCase.execute())
        }
    }
}

对应UseCase中的execute方法实现:

suspend fun execute(): Flow<MoviesStates> = flow {
    combine(f1, f2) { state1: MoviesStates, state2: MoviesStates ->
     
          // 业务处理逻辑
        
    }.collect {
        emit(it)
    }
}

测试时第一个发射值MoviesStates.Loading校验正常,但测试emitAll(moviesUseCase.execute())返回的流内容时测试失败,抛出异常:

java.util.NoSuchElementException: Expected at least one element

当前编写的单元测试代码:

@Test
fun testLoadMovies() = runBlocking {
    whenever(useCase.execute()).thenReturn(flow {
        MoviesStates.EmptyList
    })

    val actual = viewModel.handle(MoviesActions.LoadMovies).drop(1).first()
    val expected = MoviesStates.EmptyList

    assertEquals(actual, expected)
}

问题根因

测试失败的核心原因是Mock的UseCase返回的Flow没有发射任何值:
代码中flow { MoviesStates.EmptyList }只是在Flow构建块里写了MoviesStates.EmptyList的类引用,没有调用emit()方法把值推送到流中。调用drop(1).first()时,流丢弃第一个Loading状态后收集不到任何后续元素,就会抛出找不到元素的异常。

修复方案

1. 修正Mock的Flow逻辑

两种写法均可:

  • 在Flow构建块中主动调用emit发射值
whenever(useCase.execute()).thenReturn(flow {
    emit(MoviesStates.EmptyList)
})
  • 直接使用flowOf构建携带指定值的Flow,代码更简洁
whenever(useCase.execute()).thenReturn(flowOf(MoviesStates.EmptyList))

2. 可选代码优化

  • 现有UseCase的execute方法存在冗余包装:不需要手动收集combine结果再重新emit,直接返回combine生成的Flow即可,减少不必要的流嵌套,同时可以去掉多余的suspend修饰(combine本身返回冷流,不需要挂起):
fun execute(): Flow<MoviesStates> = combine(f1, f2) { state1: MoviesStates, state2: MoviesStates ->
    // 原有业务组合逻辑
}
  • 协程单元测试推荐使用runTest替代runBlocking,可以自动跳过协程延迟,提升测试执行效率。修正后的完整测试代码:
@Test
fun testLoadMovies() = runTest {
    whenever(useCase.execute()).thenReturn(flowOf(MoviesStates.EmptyList))

    val flowResult = viewModel.handle(MoviesActions.LoadMovies)
    val actual = flowResult.drop(1).first()
    
    assertEquals(MoviesStates.EmptyList, actual)
}

内容的提问来源于stack exchange,提问作者Islam Ahmed

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 16:15:33