Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

363
Visualizações
Android unit testing view model that receives flow

I have a ViewModel that talks to a use case and gets a flow back i.e Flow<MyResult>. I want to unit test my ViewModel. I am new to using the flow. Need help pls. Here is the viewModel below -

class MyViewModel(private val handle: SavedStateHandle, private val useCase: MyUseCase) : ViewModel() {

        private val viewState = MyViewState()

        fun onOptionsSelected() =
            useCase.getListOfChocolates(MyAction.GetChocolateList).map {
                when (it) {
                    is MyResult.Loading -> viewState.copy(loading = true)
                    is MyResult.ChocolateList -> viewState.copy(loading = false, data = it.choclateList)
                    is MyResult.Error -> viewState.copy(loading = false, error = "Error")
                }
            }.asLiveData(Dispatchers.Default + viewModelScope.coroutineContext)

MyViewState looks like this -

 data class MyViewState(
        val loading: Boolean = false,
        val data: List<ChocolateModel> = emptyList(),
        val error: String? = null
    )

The unit test looks like below. The assert fails always don't know what I am doing wrong there.

class MyViewModelTest {

    @get:Rule
    val instantExecutorRule = InstantTaskExecutorRule()

    private val mainThreadSurrogate = newSingleThreadContext("UI thread")

    private lateinit var myViewModel: MyViewModel

    @Mock
    private lateinit var useCase: MyUseCase

    @Mock
    private lateinit var handle: SavedStateHandle

    @Mock
    private lateinit var chocolateList: List<ChocolateModel>

    private lateinit var viewState: MyViewState


    @Before
    fun setup() {
        MockitoAnnotations.initMocks(this)
        Dispatchers.setMain(mainThreadSurrogate)
        viewState = MyViewState()
        myViewModel = MyViewModel(handle, useCase)
    }

    @After
    fun tearDown() {
        Dispatchers.resetMain() // reset main dispatcher to the original Main dispatcher
        mainThreadSurrogate.close()
    }

    @Test
    fun onOptionsSelected() {
        runBlocking {
            val flow = flow {
                emit(MyResult.Loading)
                emit(MyResult.ChocolateList(chocolateList))
            }

            Mockito.`when`(useCase.getListOfChocolates(MyAction.GetChocolateList)).thenReturn(flow)
            myViewModel.onOptionsSelected().observeForever {}

            viewState.copy(loading = true)
            assertEquals(viewState.loading, true)

            viewState.copy(loading = false, data = chocolateList)
            assertEquals(viewState.data.isEmpty(), false)
            assertEquals(viewState.loading, true)
        }
    }
}
over 4 years ago · Hanz Gallego
1 Respostas
Responde à pergunta

0

I think I have found a better way to test this, by using Channel and consumeAsFlow extension function. At least in my tests, I seem to be able to test multiple values sent throught the channel (consumed as flow).

So.. say you have some use case component that exposes a Flow<String>. In your ViewModelTest, you want to check that everytime a value is emitted, the UI state gets updated to some value. In my case, UI state is a StateFlow, but this should be do-able with LiveData as well. Also, I am using MockK, but should also be easy with Mockito.

Given this, here is how my test looks:

@Test
fun test() = runBlocking(testDispatcher) {

    val channel = Channel<String>()
    every { mockedUseCase.someDataFlow } returns channel.consumeAsFlow()

    channel.send("a")
    assertThat(viewModelUnderTest.uiState.value, `is`("a"))

    channel.send("b")
    assertThat(viewModelUnderTest.uiState.value, `is`("b"))
}

EDIT: I guess you can also use any kind of hot flow implementation instead of Channel and consumeAsFlow. For example, you can use a MutableSharedFlow that enables you to emit values when you want.

over 4 years ago · Hanz Gallego Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda