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 statewe 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 Stateare being emitted by theStateFlowor not. For a typical case, ourUI Statemay be ofEmpty, Progress, Successetc. And for that, we can useTurbinein 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 scopeturbineScopewhere 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.