I have a viewmodel class that uses a StateFlow that is created using the StateIn operator.
I.E.
private val _state = MutableStateFlow(MyState())
private val myItems = myRepository.myFlow.stateIn(
viewModelScope, SharingStarted.WhileSubscribed(), emptyList<MyObject>())
val state: StateFlow<MyState> = combine(_state, myItems ) { state, myItems ->
..///
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), MyState())
When reading the documentation and various articles and SO questions (which are outdated), I saw that you are supposed to test this by using:
backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
//Collect your flow
}
The problem I am having is that when I create a unit test to test my viewmodel, the collection of the state is not fast enough for the assertions that take place.
@Test
fun checkMyItemTest() = runTest {
val results = mutableListOf<MyState>()
val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.state.collect {
results.add(it)
}
}
viewModel.onEvent(MyEvent.DoSomething())
assert(results[0].myStateParameter) //Perform some check on results[0]
assert(results[1].myStateParameter) //Perform some check on results[1]
job.cancel()
}
The test above fails in the second assertion, since results has only one element in it.
I have gotten it to work by shoving a Thread.sleep
call of 50ms between the two assert calls, but that does not seem like the best approach to me.
@Test
fun checkMyItemTest() = runTest {
val results = mutableListOf<MyState>()
val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.state.collect {
results.add(it)
}
}
viewModel.onEvent(MyEvent.DoSomething())
assert(results[0].myStateParameter) //Perform some check on results[0]
Thread.sleep(50) /// <---- THIS
assert(results[1].myStateParameter) //Perform some check on results[1]
job.cancel()
}
I am not interested in using Turbine at the moment
So I am looking to understand how I can test my flow properly and how I can collect the values emitted by it.
I am also a newbie to testing but from what I have read runTest{}
skips delay and is not much diffrent from runBlocking so this might work:
@Test
fun checkMyItemTest() = runTest {
val results = Channel<MyState>() // this is a rendevous channel
val job = backgroundScope.launch(UnconfinedTestDispatcher(testScheduler)) {
viewModel.state.collect {
results.send(it)
}
}
viewModel.onEvent(MyEvent.DoSomething())
val result1 = channel.receive() //suspends till it has a corresponding send call
val result2 = channel.receive()
assert(result1.myStateParameter) //Perform some check on result1
assert(result2.myStateParameter) //Perform some check on result2
}
I have not run the code.