# Exercises in futility: Unit-testing LiveData, ViewModels and Coroutines

21 Jan 2020  
2 min read  
android  
https://www.costafotiadis.com/exercises-in-futility-unit-testing-livedata-viewmodels-and-coroutines/  
human written · 100% human · [Pangram analysis](https://www.pangram.com/history/8ae6c51e-0a14-4df9-a980-d01df5d0e1fc)

---

_This is part of a series head-scratching my way into coroutines. It can be read as a standalone although you might be missing out on some spicy memes_ [_here_](https://medium.com/@con.fotiadis/kotlin-coroutines-review-53e951c4a0fa) _and_ [_here_](https://medium.com/@con.fotiadis/on-testing-kotlin-coroutines-d19b69d138f1)_._

### Testing ViewModels without losing the will to live

A ViewModel “sits” quite close to the activity/fragment. Something that a user would be looking at generally, even if they are unaware.

You would think that a professional keyboard user walking into your ViewModel test file would _probably_ be able to read the test methods and make some sense of what’s going on at the screen level without even running your app, right? 🤡

### Get some dependencies first

```groovy
dependencies {
    // .. other dependencies
    // ..

    // testing dependencies
    testImplementation 'androidx.test.ext:junit:1.1.2-alpha03'
    testImplementation 'org.mockito:mockito-core:3.0.0'
    testImplementation 'com.nhaarman.mockitokotlin2:mockito-kotlin:2.1.0'
    testImplementation 'org.mockito:mockito-inline:3.0.0'
    testImplementation 'org.amshove.kluent:kluent:1.51'
    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.3.2'
    testImplementation 'androidx.arch.core:core-testing:2.1.0'


}
```

*build.gradle*

### Where we left off

Our little ViewModel looks like this:

```kotlin
class PostViewModel(private val redditRepository: RedditRepository) : ViewModel() {

    val stateData = MutableLiveData<State>()

    fun getRedditPost() {
        viewModelScope.launch {
            stateData.value = State.Loading
            val post: State.Post = redditRepository.getPost()
            stateData.value = post
        }
    }
}

sealed class State {
    object Loading : State()
    data class Post(val text: String) : State()
}
```

*PostviewModel.kt*

Most android apps out there are based on this simple premise.

The _PostViewModel_ is responsible for fetching a post from Reddit while the calling activity/fragment is responsible for observing the _stateData_ variable.

Googling “_LiveData testing how to_” (and its myriad of variations), you’ll find all sorts of smart extension functions ([_LiveDataTestUtil_](https://github.com/android/architecture-components-samples/blob/master/LiveDataSample/app/src/test/java/com/android/example/livedatabuilder/util/LiveDataTestUtil.kt) and others like it) that hide what’s going on and are not really that useful on a number of occasions.

A simple solution is to use a _LifeCycleTestOwner_ helper class. (at the cost of adding a few extra lines of code that is)

```kotlin
class LifeCycleTestOwner : LifecycleOwner {

    private val registry = LifecycleRegistry(this)

    override fun getLifecycle(): Lifecycle {
        return registry
    }

    fun onCreate() {
        registry.handleLifecycleEvent(Lifecycle.Event.ON_CREATE)
    }

    fun onResume() {
        registry.handleLifecycleEvent(Lifecycle.Event.ON_RESUME)
    }

    fun onDestroy() {
        registry.handleLifecycleEvent(Lifecycle.Event.ON_DESTROY)
    }
}
```

*LifeCycleTestOwner.kt*

We’ll use this to replicate the presence of an activity or fragment in our test class.

### The setup

```kotlin
@ExperimentalCoroutinesApi
class PostViewModelTest {
    @get:Rule
    val coroutineTestRule = CoroutineTestRule()
    @get:Rule
    var rule: TestRule = InstantTaskExecutorRule()

    private val stateObserver: Observer<State> = mock()
    private val redditRepository: RedditRepository = mock()

    private lateinit var lifeCycleTestOwner: LifeCycleTestOwner
    private lateinit var postViewModel: PostViewModel

    @Before
    fun setUp() {
        lifeCycleTestOwner = LifeCycleTestOwner()
        lifeCycleTestOwner.onCreate()
        postViewModel = PostViewModel(redditRepository)
        postViewModel.stateData.observe(lifeCycleTestOwner, stateObserver)
    }

    @After
    fun tearDown() {
        lifeCycleTestOwner.onDestroy()
    }
}
```

*PostViewModelTest.kt*

-   Using the [_CoroutineTestRule_](https://gist.github.com/CostaFot/b690f92be527c899f8321d434804430e) we have full control of running everything on the _TestCoroutineDispatcher._ You can find more on this rule [here](https://medium.com/@con.fotiadis/on-testing-kotlin-coroutines-d19b69d138f1).
-   [_InstantTaskExecutorRule_](https://developer.android.com/reference/androidx/arch/core/executor/testing/InstantTaskExecutorRule) comes bundled in the androidx.arch.core:core-testing library and should be used when testing LiveData.
-   The _stateObserver_ variable is just a mock. Think of it as the observer in the activity/fragment.
-   The _lifeCycleTestOwner_ plays the role of the lifecycle of the activity/fragment and is created and destroyed before each test.

![](https://www.costafotiadis.com/images/2020/01/1-5-QCriHlvtyKMK3DBi8uTw.jpeg)

### Testing?

Unit tests **do** have their place, although they can get a bit overboard. A rather sad test on this occasion really goes for isolation and tests every little thing that can happen separately.

```kotlin
@Test
    fun `a useless test for getRedditPost`() {
        coroutineTestRule.testDispatcher.runBlockingTest {
            // Given
            lifeCycleTestOwner.onResume()
            val redditPost = State.Post("This is a reddit post")
            When calling redditRepository.getPost() itReturns redditPost
            // When
            postViewModel.getRedditPost()
            // Then
            Verify on redditRepository that redditRepository.getPost() was called
            Verify on stateObserver that stateObserver.onChanged(State.Loading) was called
        }
    }
```

*sad\_test.kt*

While this test will pass and it **does** verify a certain behavior, we can do better and provide some sort of documentation and high level view of the ViewModel to a stranger who doesn’t really know what going on in that file.

```kotlin
 @Test
    fun `geRedditPost shows loading first then shows the post after it was successfully fetched`() {
        coroutineTestRule.testDispatcher.runBlockingTest {
            // Given
            lifeCycleTestOwner.onResume()
            val redditPost = State.Post("This is a reddit post")
            When calling redditRepository.getPost() itReturns redditPost
            // When
            postViewModel.getRedditPost()
            // Then
            Verify on stateObserver that stateObserver.onChanged(State.Loading) was called
            Verify on stateObserver that stateObserver.onChanged(redditPost) was called
            VerifyNoFurtherInteractions on stateObserver
        }
    }
```

*happy\_test.kt*

### Wew lad

Unit tests will never replace decent **UI** tests on android (or your userbase throwing 1-star reviews on your app because it’s crashing all over the place), but this is good enough to build on for more complex screens. Much of the verbosity can also be decreased by using extension functions and test rules.

Later.
