I am working on Android project and making API calls to fetch data from the server. I am using Retrofit for networking and StateFlow for storing and passing data to the UI layer.
It is working perfectly fine, behavior-wise.
I wrote a unit test for ViewModel for Success, Failure, but I am getting a Coroutine timeout error and it looks like something is leaking from Coroutine. I am not completely sure. Any help would be appreciated.
I also checked few answers from StackOverflow and it's not helpful
- Getting kotlin error "After waiting for 60000 ms, the test coroutine is not completing"
- Asserting a coroutine is not complete under unit test
I am also open to rewrite test cases for viewmodel that covers Loading, Success & Failure scenarios.
ERROR
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)
ViewModel
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))
}
}
}
}
}
Unit Test
@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())
}