Skip to content

Instantly share code, notes, and snippets.

@laaptu
Last active July 5, 2023 02:00
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save laaptu/a543c733c762f5b2be5a8829f115f8ba to your computer and use it in GitHub Desktop.
Save laaptu/a543c733c762f5b2be5a8829f115f8ba to your computer and use it in GitHub Desktop.

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 final UI 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 the StateFlow or not. For a typical case, our UI State may be of Empty, Progress, Success etc. And for that, we can use Turbine 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 scope turbineScope where can use testIn() 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment