Unit-testing LiveData and other common observability problems

Jose Alcérreca
Sep 2, 2019 · 3 min read

Next time you’re scratching your head wondering why an innocent looking unit test with LiveDatas is failing, or staring at an empty screen that should show… something, you should remember that a LiveData that is not observed won’t emit updates. In this post you’ll learn some good practices to avoid this problem.

This is especially important when dealing with Transformations. If a LiveData is transformed, the result of the transformation must be observed. Otherwise the transformation will never be evaluated.

If a LiveData falls in a forest and no one is observing it… was it ever updated?

In this example, the value of the initial _liveData1 is transformed (mapped) to upper case whenever the initial value changes:

Let’s write a simple test for it:

This test fails because LiveData doesn’t do more work than needed. Reading liveData2.valuedoesn’t initiate the chain of dependent transformations because it’s not observed. Only a subscription through observe() does that.

Image for post
Image for post
The result of a Transformation is not computed if it’s not observed

Note that liveData1’s value can be read because it’s a MutableLiveData and no transformations need to be evaluated.

A simple (but not great) way to fix this issue would be to call observeForever() on the LiveDatas that we need to read:

Image for post
Image for post
The result of a Transformation is not computed if it’s not observed

However if we need to observe multiple LiveDatas this can fill our tests quickly with unimportant statements.

To fix this, Jetpack doesn’t provide a test helper yet, but we can make our own:

Also available in Java.

This function observes a LiveData until it receives a new value (via onChanged) and then it removes the observer. If the LiveData already has a value, it returns it immediately. Additionally, if the value is never set, it will throw an exception after 2 seconds (or whatever you set). This prevents tests that never finish when something goes wrong.

Alternatively, if you need to keep observing a LiveData throughout a test, because it might receive multiple values that you want to check, take a look at observeForTesting in the LiveData sample.

Now the test is much more readable:

Note: While you don’t need to do this with MutableLiveDatas, it’s good practice to getOrAwaitValue when reading any LiveData value in case the implementation detail changes in the future:

Image for post
Image for post
You don’t need to observe MutableLiveDatas to get their value, but it’s a good practice.

InstantTaskExecutorRule

It’s important to note that this technique can have threading issues. Most of them can be solved by adding InstantTaskExecutorRule to your unit tests. However, if you call LiveData.postValue() from the main thread, the documented precedence might not be preserved. This is not common but worth mentioning because it can create evasive bugs.

See it in action in this test in the LiveData sample.

Other scenarios

Wrong ViewModel

If you’re using a shared ViewModel between multiple fragments, make sure you’re using the same instance in all screens. This can happen when passing the Fragment instead of the Activity as the LifecycleOwner to the ViewModelProviders or using by ViewModels in a fragment instead of by activityViewModels().

Wrong Room database instance

Why is a query that returns a LiveData not emitting any updates?

If you’re sure that the LiveData is being observed, you might be using different instances of the database. Check your creation patterns or your DI graph. If you know what you’re doing, you can also enable multi-instance invalidation in the database builder, but it has some limitations.

You can see LiveDataTestUtil in action in the LiveDataSample.

What other scenarios have you found? Let us know in the comments!

Android Developers

The official Android Developers publication on Medium

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store