Effective LiveData and ViewModel Testing

Many has been written about Architecture Components and how to implement them, having one of the benefits improved testability. Ok, but how does it actually improve? Let’s have a look.

Architecture Components were introduced on Google I/O 2017 and they meant significant improvement in Android development world. Best practices learned over years were transformed into libraries handling most common problems and opinionated guide was out.

View and ViewModel communicates via LiveData

One of the key thoughts of Architecture Components is Observer Pattern for updating Activity or Fragment to handle commonly experienced problems.

Important part here is View remains passive, keeping all the logic in ViewModel, also dispatching user actions immediately to ViewModel. Complexity to verify by test should remain in ViewModel and if possible, we should avoid android.* dependencies to be able to test with pure JUnit test. This is important to achieve Effective ViewModel Test.

Effective ViewModel Test

We could discuss for ages, which test is effective and worth to write, but lets now set some goals we want our tests to achieve:

Very fast test execution Modelling real use case of ViewModel under test Test is easy to write and maintain

Resolving first requirement means keeping the test in JUnit only, without using Robolectric or instrumentation test. This is possible to achieve and potentially worth the effort.

Discussing requirement 2. brings us to the point why following Architecture Components makes sense in case of improved testing. Separation of components makes easy to replace Activity by something else. Test can behave as Activity whilst keeping the test modelling real use case of interaction with ViewModel.

Now let’s discuss the last requirement of easy writing and maintenance.

Writing effective ViewModel test

During the first presentation of LiveData and ViewModel components, expected question was soon raised: “Is this RxJava?”. It is not, but there are clearly some common patterns.

Having this in mind, together with the fact that RxJava is brilliant and very well tested library, we can take inspiration from testing approach there. Each type has a method test() returning TestObserver or TestSubscriber to perform fluent test assertions.

We can implement our own TestObserver for LiveData with required assert methods and since we now wield tools like Kotlin extension methods, we can achieve same comfort as RxJava to have test() method on LiveData directly. Code using livedata-testing library will look like this:

viewModel.counterLiveData()

.test()

.assertHasValue()

.assertValue { it > 3 }

.assertValue(4)

.assertNever { it > 4 } viewModel.plusButtonClicked() // internally increments counter viewModel.counterLiveData()

.test()

.assertValue(5)

.assertNever { it > 5}

...

Implementation of this uses simple extension method to subscribe to LiveData and return TestObserver having introduced assertion methods.

fun <T> LiveData<T>.test(): TestObserver<T> {

return TestObserver.test(this)

}

TestObserver is Java class, so you can use this approach even if your project is using only Java without Kotlin. The only difference would be calling static method:

TestObserver.test(viewModel.counterLiveData())

.assertHasValue()

...;