我正在Android项目上工作,并进行API调用以从服务器获取数据。我使用Retrofit进行网络连接,使用StateFlow存储数据并将数据传递到UI层。
它运行得非常好,行为明智。
我为ViewModel写了一个单元测试,成功,失败,但是我得到了一个协程超时错误,看起来像是有什么东西从协程中泄漏出来。我也不是很确定。任何帮助将不胜感激。
我还检查了StackOverflow的一些答案,但没有帮助
- Getting kotlin error "After waiting for 60000 ms, the test coroutine is not completing"
- Asserting a coroutine is not complete under unit test
我也愿意为视图模型重写测试用例,包括加载,成功和失败场景。
错误
After waiting for 60000 ms, the test coroutine is not completing
kotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 60000 ms, the test coroutine is not completing
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTestCoroutine$3$3.invokeSuspend(TestBuilders.kt:342)
(Coroutine boundary)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt.runTestCoroutine(TestBuilders.kt:326)
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTest$1$1.invokeSuspend(TestBuilders.kt:167)
at kotlinx.coroutines.test.TestBuildersJvmKt$createTestResult$1.invokeSuspend(TestBuildersJvm.kt:13)
Caused by: kotlinx.coroutines.test.UncompletedCoroutinesError: After waiting for 60000 ms, the test coroutine is not completing
at kotlinx.coroutines.test.TestBuildersKt__TestBuildersKt$runTestCoroutine$3$3.invokeSuspend(TestBuilders.kt:342)
at app//kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
视图模型
private val _uiCaseListState = MutableStateFlow<Resource<List<Case>>?>(null)
val uiCaseListState: StateFlow<Resource<List<Case>>?> get() = _uiCaseListState
fun fetchCaseList() {
viewModelScope.launch(Dispatchers.IO) {
inboxRepository.fetchCaseList().collect { response ->
when {
response.isSuccessful -> {
response.body()?.let { caseList ->
val filterCaseList = caseList.sortedByDescending { case ->
case.createdDate.time
}
_uiCaseListState.emit(Resource.Success(filterCaseList))
}
}
!response.isSuccessful -> {
_uiCaseListState.emit(Resource.Failure(ErrorCode.GENERAL_ERROR))
}
else -> {
_uiCaseListState.emit(Resource.Failure(ErrorCode.GENERAL_ERROR))
}
}
}
}
}
单元测试
@Test
fun fetchCaseList_Success_CaseList() = runTest {
// Arrange
val repo = InboxRepository(object : InboxApi {
override suspend fun fetchCaseList(): Response<List<Case>?> {
return Response.success(testCaseList)
}
})
val vm = InboxViewModel(repo)
// Act
vm.fetchCaseList()
// Assert
assertEquals(Resource.Success(testCaseList).data.size, vm.uiCaseListState.drop(1).first()!!.data!!.size)
}
/**
* "kotlinx.coroutines.test.UncompletedCoroutinesError:After waiting for 60000 ms, the test coroutine is not completing"
*/
@Ignore
@Test
fun fetchCaseList_Failure_GENERAL_ERROR() = runTest {
// Arrange
val error: Response<List<Case>?> = Response.error(
400,
"{\"key\":[\"someError\"]}"
.toResponseBody("application/json".toMediaTypeOrNull())
)
val repo = InboxRepository(object : InboxApi {
override suspend fun fetchCaseList(): Response<List<Case>?> {
return error
}
})
val vm = InboxViewModel(repo)
// Act
vm.fetchCaseList()
// Assert
assertEquals(Resource.Failure<Error>(ErrorCode.GENERAL_ERROR), vm.uiCaseListState.drop(1).first())
}
1条答案
按热度按时间gxwragnw1#
代码的问题是在测试时没有办法替换dispatcher。如果任务运行在您无法控制的后台线程上,则很难在正确的时间执行Assert或等待任务完成。
解决方案是通过构造函数在viewModel中注入dispatcher:
在测试中,您可以注入
TestDispatcher
来替换Dispatchers.IO
。