It seems that the Android team changed the API and documentation after this thread. You can check it here: Continuous collection
SharedFlow/StateFlow is a hot flow, and, as described in the docs, A shared flow is called hot because its active instance exists independently of the presence of collectors.
It means the scope that launches the collection of your flow won’t complete by itself.
To solve this issue, you need to cancel the scope in which collect
is called, and as the scope of your test is the test itself, it’s not ok to cancel the test, so what you need is to launch
it in a different job.
@Test
fun `Testing a integer state flow`() = runTest {
val _intSharedFlow = MutableStateFlow(0)
val intSharedFlow = _intSharedFlow.asStateFlow()
val testResults = mutableListOf<Int>()
val job = launch(UnconfinedTestDispatcher(testScheduler)) {
intSharedFlow.toList(testResults)
}
_intSharedFlow.value = 5
assertEquals(2, testResults.size)
assertEquals(0, testResults.first())
assertEquals(5, testResults.last())
job.cancel()
}
Improved case:
TestScope.backgroundScope ensures that the coroutine gets cancelled before the end of the test.
@Test
fun `Testing an integer state flow`() = runTest {
val _intSharedFlow = MutableStateFlow(0)
val intSharedFlow = _intSharedFlow.asStateFlow()
val testResults = mutableListOf<Int>()
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
intSharedFlow.toList(testResults)
}
_intSharedFlow.value = 5
assertEquals(2, testResults.size)
assertEquals(0, testResults.first())
assertEquals(5, testResults.last())
}
A few important things:
- Always cancel your created job to avoid
java.lang.IllegalStateException: This job has not completed yet
- As this is a
StateFlow
, when you start collecting (insidetoList
), you receive the last state. But if you first start collecting and then call your functionviewModel.getUserWallets()
, theresult
list will then have all the states, in case you want to test it too. - The
runTest
API changed a little bit, and we need to useUnconfinedTestDispatcher(testScheduler)
in the context of thelaunch
call. The documentation says:Notice how UnconfinedTestDispatcher is used for the collecting coroutine here. This ensures that the collecting coroutine is launched eagerly and is ready to receive values after launch returns.