Testing a ViewModel

Testing Mid-level Kotlin 2.0+ API 24+ Reviewed By Best Latest Apps

Order the steps

Order a ViewModel error-path test.

  • Create a fake repository set to fail
  • Construct the ViewModel with the fake
  • Assert UiState shows the error and isn't loading
  • Set Main to a test dispatcher with a rule
  • Call onRefresh() and advance until idle

Choose an answer, then open Show answer below to check it.

Show answer

Answer: D A B E C

  1. Set Main to a test dispatcher with a rule viewModelScope uses Main.
  2. Create a fake repository set to fail Controls the error path.
  3. Construct the ViewModel with the fake Constructor injection makes this easy.
  4. Call onRefresh() and advance until idle Run the coroutine.
  5. Assert UiState shows the error and isn't loading Check what the user would see.

30-second answer

Construct it with fakes for its dependencies and a SavedStateHandle you create in the test, replace Main with a test dispatcher, call its event functions, and assert on the UiState it exposes. Cover the loading, success, empty and error paths. No Android device is needed, because the ViewModel holds no views or Activity.

Practise saying it · 30 s. No recording, just a timer

Explain how to test a ViewModel.

Say your answer out loud, then check which of these points you covered:

  • Fakes and a test SavedStateHandle
  • Test dispatcher for Main
  • Call events, assert UiState
  • Cover all states

Deep dive

SavedStateHandle(mapOf("id" to "42")) supplies arguments.

Make the fake fail on demand to test errors.

Advance virtual time for debounces or timeouts.

Test what the user sees (state), not private functions.

A ViewModel test Compile-checked · Kotlin 2.4.21
class FeedViewModelTest {
    @get:Rule val main = MainDispatcherRule()

    @Test
    fun refreshShowsItems() = runTest {
        val vm = FeedViewModel(load = { listOf(Article("1", "Hello")) })
        vm.onRefresh()
        assertEquals(listOf(Article("1", "Hello")), vm.state.value.items)
        assertFalse(vm.state.value.isRefreshing)
    }
}

Common mistakes

  • Testing private methods via reflection.
  • Using real repositories that hit the network.
  • Asserting only the happy path.

Senior take & follow-ups

Walk through arrange/act/assert with a fake, and name all four states.

Follow-up questions

Think of your answer first, then open the hint.

  1. How do you pass navigation args in a test?

    Show hint

    Create SavedStateHandle with a map of values.

  2. Do you need Robolectric?

    Show hint

    No, a ViewModel test is a plain JVM test.

Suggest a fix if something here is wrong or out of date.