如何在协程中测试获取StateFlow全部状态值(无需delay)
StateFlow测试无法捕获加载状态的解决方案
测试StateFlow时,始终仅能接收到初始状态和成功状态,无法获取LoaderType对应的加载状态;若不使用delay()函数,测试用例会持续失败。以下是无需依赖delay()即可成功获取所有状态的测试方案:
失败的实现代码
fun getExhibitions(loaderType: LoaderType) { viewModelScope.launch { state.update { it.loading(loaderType) } // #1 切换为加载状态 val result = useCase.getExhibitions( params = GetExhibitionsUseCase.Params(shopId, sellerId) ) when (result) { is UseCaseResult.Success -> { val displayModel = mapper.mapExhibitionList(result.value) state.update { it.loadingSuccess(displayModel.exhibitions) } // #2 切换为成功状态 } is UseCaseResult.Fail -> { onErrorGetExhibitions(result.throwable) } } } }
测试代码
@Test fun load_success() = runTest { // given prepareViewModel_success() viewModel.uiState.test { // 初始状态断言 assert((awaitItem() as UiState.Loading).loaderType == QCommerceLoaderType.NONE) // 触发加载 viewModel.getExhibitions(loaderTyp = LoaderType.TEXT) // 验证状态 assert((awaitItem() as UiState.Loading).loaderType == QCommerceLoaderType.TEXT) // 此处测试失败 assert((awaitItem() as UiState.Success).exhibitions.isNotEmpty()) } }
可行的修改方案
方案一:添加delay()(不推荐)
通过强制延迟让状态更新有时间被测试捕获,但这种方案依赖时间,稳定性差,不建议使用:
fun getExhibitions(loaderType: LoaderType) { viewModelScope.launch { state.update { it.loading(loaderType) } // #1 切换为加载状态 delay(500) // 新增延迟代码 val result = useCase.getExhibitions( params = GetExhibitionsUseCase.Params(shopId, sellerId) ) when (result) { is UseCaseResult.Success -> { val displayModel = mapper.mapExhibitionList(result.value) state.update { it.loadingSuccess(displayModel.exhibitions) } // #2 切换为成功状态 } is UseCaseResult.Fail -> { onErrorGetExhibitions(result.throwable) } } } }
方案二:调整状态更新位置(推荐)
将加载状态的更新移出协程,直接在函数调用时同步执行。这样状态更新会立即被StateFlow分发,测试代码能稳定捕获到加载状态:
fun getExhibitions(loaderType: LoaderType) { state.update { it.loading(loaderType) } // 调整状态更新到协程外部 viewModelScope.launch { val result = useCase.getExhibitions( params = GetExhibitionsUseCase.Params(shopId, sellerId) ) when (result) { is UseCaseResult.Success -> { val displayModel = mapper.mapExhibitionList(result.value) state.update { it.loadingSuccess(displayModel.exhibitions) } // #2 切换为成功状态 } is UseCaseResult.Fail -> { onErrorGetExhibitions(result.throwable) } } } }
内容的提问来源于stack exchange,提问作者Jayden
相关产品推荐
相关产品推荐

