How to Unit Test LiveData and ViewModel

Source code can be found here.

Import dependencies

implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0'
testImplementation 'androidx.arch.core:core-testing:2.0.1'
testImplementation 'org.mockito:mockito-core:2.28.2'

Check the official Android documentation for the latest version of these Android packages.

For Mockito, check out their Github repository for the latest version.

Create a simple User class

data class User(val id: Int)

Create a ViewModel

class MainViewModel : ViewModel() {

    private val _user = MutableLiveData<User>()

    val user: LiveData<User>
        get() = _user

    fun fetchUser(id: Int) {
        val user = User(id)

        _user.value = user
    }
}

Create a helper function to mock classes with types (generics)

Create a kotlin file - MockitoUtils.kt inside your test folder.

inline fun <reified T> mock(): T = Mockito.mock(T::class.java)

Creating the unit test

class MainViewModelTest {

    @get:Rule
    val rule = InstantTaskExecutorRule()

    private lateinit var viewModel: MainViewModel

    private val observer: Observer<User> = mock()

    @Before
    fun before() {
        viewModel = MainViewModel()
        viewModel.user.observeForever(observer)
    }

    @Test
    fun fetchUser_ShouldReturnUser() {
        val expectedUser = User(1)

        viewModel.fetchUser(expectedUser.id)

        val captor = ArgumentCaptor.forClass(User::class.java)
        captor.run {
            verify(observer, times(1)).onChanged(capture())
            assertEquals(expectedUser, value)
        }
    }
}

@get:Rule
val rule = InstantTaskExecutorRule()

What this rule basically does is allow us to run LiveData synchronously. This rule is from the core-testing package that was imported earlier.

private val observer: Observer<User> = mock()

The helper function that we made will help us be able to mock our Observer. Try mocking it the standard way and you’ll see what I mean - mock(Observer<User>::class.java).

val captor = ArgumentCaptor.forClass(User::class.java)
captor.run {
    verify(observer, times(1)).onChanged(capture())
    assertEquals(expectedUser, value)
}

ArgumentCaptor does what the class name is called - capture argument(s). We capture() the argument when the onChanged(...) method of our Observer is called.

Adding verify(observer, times(1)).onChanged(capture()) allows you to capture instances where the LiveData is called multiple times when you expect it to be called only once.

Assert that the expectedUser that we’ve created is equal to the emitted user of our LiveData.

Lastly, run the goddamn test.

If you’ve found this helpful, subscribe to my newsletter below to be updated with the latest posts and get a free book on the 7 ways to become a really good Android developer!👇

7 ways to become a really good Android developer