androidkotlinunit-testingandroid-viewmodelturbine

Best practice for testing the initial state of a ViewMode with Turbine


I have a simple ViewModel with a state that keeps some data. State is initialized in the init function of my ViewModel:

ViewModelState.kt

data class ViewModelState(
    val myInfo: String = "",
    val mySecondInfo: String = ""
)

MyViewModel.kt

class MyViewModel(
    private val client: Client
): ViewModel() {

    private val _state = MutableStateFlow(ViewModelState())
    val state = _state.asStateFlow()

    init {
        loadMyInfoFromClient()
        loadMySecondInfoFromClient()
    }

    private fun loadMyInfoFromClient() {
        viewModelScope.launch {
            val retrievedData = client.suspendFunction()
            _state.update {
                it.copy(myInfo = retrievedData)
            }
        }
    }

    private fun loadMySecondInfoFromClient() {
        // same thing as loadMyInfoFromClient()
    }
}

When it comes to test that ViewModel using Turbine, what is the best practice to await for different states to be emitted and check the values? Today I use several chained awaitItem() but it seems ugly to me.

fun `test state initialization`() = runTest {
            // Given
            prepareMockForClient()

            // When
            val underTest = MyViewModel(mockedClient)

            // Then
            underTest.state.test {
                val firstState = awaitItem()
                assertThat(firstState.myInfo).isEqualTo("blabla")

                val secondState = awaitItem()
                assertThat(secondState.mySecondInfo).isEqualTo("bleble")
            }
        }

I didn't find a way to wait for the last emitted state in Turbine so that I could check the whole state with something like:

underTest.state.test {
    val state = awaitLastItem()
    assertThat(state.myInfo).isEqualTo("blabla")
    assertThat(state.mySecondInfo).isEqualTo("bleble")
}

Except coding myself that awaitLastItem(), is there a better practice in Android to test the initial state of your ViewModel?

Thank you!


Solution

  • if someone's looking for an answer. I ended up using ideas proposed in the following articles: here and here.

    1. I moved the data loading part from the init{} to a public method that I call in onCreated() method.
    2. I tested loading methods separately so that only state updates from this method appear in my turbine test.
    3. I also leverage advanceUntilIdle() method to get the final state to check that the whole state has been correctly set after calling the public method initializing the state.

    Code is more robust and testing strategy is easier now. Hope it can help.