loading...

How to Unit Test LiveData and ViewModel

arthlimchiu profile image Arth Limchiu Updated on ・2 min read

Originally published on my blog.

Source code

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.

Posted on by:

arthlimchiu profile

Arth Limchiu

@arthlimchiu

Project-based Android Books @ https://gumroad.com/arthlimchiu | Be humble, keep learning, and share.

Discussion

markdown guide