Business
Jobs
  • About Us
  • Solutions
    • Job Postings
      Post your job and receive qualified candidates in 48h.
    • Candidate Assessments
      500+ technical and psychological tests, plus anti-fraud.
    • Headhunting
      Tailor-made executive search from start to finish.
    • Payroll + EOR
      Payroll dispersal and EOR across 15+ LATAM countries.
  • Pricing
  • Jobs

0

354
Views
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 answers
Answer question

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 Report
Answer question
Find remote jobs

Discover the new way to find a job!

Top jobs
Top job categories
Business
Post vacancy Pricing Sales
Legal
Terms and conditions Privacy policy
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Show me some job opportunities
There's an error!