1. Testing the most recent or latest emitted item :
- In our tests, in most of the cases, we are only interested in the last emitted value.For instance, for any
UI state
we are mostly interested on what will be the finalUI state
. And to test the last emitted value, we can do the following
@Test
fun verifyErrorOnUpcomingShiftsFetchUIState() = runDeputyTest {
// given fetched result is failure
coEvery { getGroupedUpcomingShifts(request) } returns Failure()
// when
viewModel.performAction(FetchUpcomingShifts)
// then
viewModel.uiState.test {
expectMostRecentItem() shouldBeEqualTo ErrorOnUpcomingShiftsFetch
}
}
- The main advantage of this here is that we don't have to use any collectors to collect the value and test it
2. Testing all the emitted values by the state flows:
- In our test case we may required to know whether all the
UI State
are being emitted by theStateFlow
or not. For a typical case, ourUI State
may be ofEmpty, Progress, Success
etc. And for that, we can useTurbine
in following ways
@Test
fun verifyErrorOnUpcomingShiftsFetchUIState() = runDeputyTest{
// given fetched result is failure
coEvery { getGroupedUpcomingShifts(request) } coAnswers {
delay(defaultDelayInMillis)
Failure()
}
viewModel.uiState.test {
// at initialization of state flow Empty state
awaitItem() shouldBeEqualTo Empty
// when we fetch upcoming shifts
viewModel.performAction(FetchUpcomingShifts)
// then the UI state changes to Progress
awaitItem() shouldBeEqualTo ProgressOnUpcomingShiftsFetch
// advancing time to emit failure
advanceTimeBy(defaultAdvanceTimeByInMillis)
// at last our UI state is Error
awaitItem() shouldBeEqualTo ErrorOnUpcomingShiftsFetch
}
}
}
This gives us the advantage of testing the UI State
as per emitted order.
But if we just want to collect all of the emitted item at once and don't want to test the order, then we can use normal List
to collect emitted values without using the Turbine
. Maybe Turbine
also has this, but I haven't explored it yet
@Test
fun verifyErrorOnUpcomingShiftsFetchUIState() = runDeputyTest {
// given fetched result is failure
coEvery { getGroupedUpcomingShifts(request) } coAnswers {
delay(defaultDelayInMillis)
Failure()
}
// we collect all the uiStates emitted by the viewModel
val uiStates = mutableListOf<UpcomingShiftsUIState>()
testScope.launch {
viewModel.uiState.toList(uiStates)
}
// when
viewModel.performAction(FetchUpcomingShifts)
advanceTimeBy(defaultAdvanceTimeByInMillis)
// then uiStates must be emitted in following order
uiStates shouldBeEqualTo mutableListOf(
Empty,
ProgressOnUpcomingShiftsFetch,
ErrorOnUpcomingShiftsFetch
)
}
}
3. Testing two or more flows at once :
- There may be usecase where we need to test more than one flow at once. And for the with
Turbine 1.0.0
, they have introduced a scopeturbineScope
where can usetestIn()
to act as a collector. And we can test two or more flows in following manner
@Test
fun verifyErrorOnUpcomingShiftsFetchUIState() = runDeputyTest {
turbineScope {
// given fetched result is failure
coEvery { getGroupedUpcomingShifts(request) } returns Failure()
// we want to know the values emitted by two state flows
// UI state flow
val uiStates = viewModel.uiState.testIn(this)
// Refresh Progress flow
val refreshProgress = viewModel.showProgressOnRefresh.testIn(this)
// when
viewModel.performAction(FetchUpcomingShifts)
// then we can test two individual flows like following
uiStates.apply {
expectMostRecentItem() shouldBeEqualTo ErrorOnUpcomingShiftsFetch
cancel()
}
refreshProgress.apply {
expectMostRecentItem() shouldBeEqualTo false
cancel()
}
}
}
We can also use the collector
to retrieve emitted item in order
@Test
fun verifyErrorOnUpcomingShiftsFetchUIState() = runDeputyTest {
turbineScope {
// given fetched result is failure
coEvery { getGroupedUpcomingShifts(request) } coAnswers {
delay(defaultDelayInMillis)
Failure()
}
// we want to know the values emitted by two state flows
// UI state flow
val uiStates = viewModel.uiState.testIn(this)
// Refresh Progress flow
val refreshProgress = viewModel.showProgressOnRefresh.testIn(this)
// when
viewModel.performAction(FetchUpcomingShifts)
advanceTimeBy(defaultAdvanceTimeByInMillis)
// then we can test two individual flows like following
uiStates.apply {
awaitItem() shouldBeEqualTo Empty
awaitItem() shouldBeEqualTo ProgressOnUpcomingShiftsFetch
awaitItem() shouldBeEqualTo ErrorOnUpcomingShiftsFetch
cancel()
}
refreshProgress.apply {
expectMostRecentItem() shouldBeEqualTo false
cancel()
}
}
}
If needed we can create our custom scope similar to testScope
for turbineScope
and then use testIn()
directly with that scope.