Search code examples
androidkotlinandroid-roomandroid-architecture-componentsandroid-livedata

Android LiveData in androidTest returns null


I'm running an androidTest instrumentation test and I have a method that returns LiveData from a DAO object using Room.

I'm calling the method like so:

val animal = roomDatabase.animalsDao().getAnimal(1)
animal.observeForever(mMockObserver)
assertNotNull(animal.value)

I used Mockito to mock the observer:

@Mock
private lateinit var mMockObserver = Observer<Animal>

This should return an instance of LiveData containing the Animal at id 1, but it's null. It's my understanding that in order for LiveData to return anything, there must be an observer. Did I set this up incorrectly?

Note: If I change the signature of getAnimal() in the DAO to return an Animal directly, rather than a LiveData, then it works so I know it's something with LiveData.


Solution

  • After a little more digging I've found a utility method Google provided through their Architecture Components examples on GitHub.

    LiveDataTestUtil

    public class LiveDataTestUtil {
    
        /**
         * Get the value from a LiveData object. We're waiting for LiveData to emit, for 2 seconds.
         * Once we got a notification via onChanged, we stop observing.
         */
        public static <T> T getValue(final LiveData<T> liveData) throws InterruptedException {
            final Object[] data = new Object[1];
            final CountDownLatch latch = new CountDownLatch(1);
            Observer<T> observer = new Observer<T>() {
                @Override
                public void onChanged(@Nullable T o) {
                    data[0] = o;
                    latch.countDown();
                    liveData.removeObserver(this);
                }
            };
            liveData.observeForever(observer);
            latch.await(2, TimeUnit.SECONDS);
            //noinspection unchecked
            return (T) data[0];
        }
    }
    

    This allows you to pass the LiveData instance and get back the value it holds.

    Update (JUnit 4):

    You can also use the InstantTaskExecutorRule combined with observeForever to test your LiveData. In Kotlin you set @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() at the top of your test class to ensure LiveData is handled synchronously, then inside your test cases myLiveData.observeForever { /* Do something when event emitted */ } to get the LiveData value.

    Update (JUnit 5)

    If you're using JUnit5, then you can use this extension instead of the Rule explained in Update (JUnit4) above.

    class InstantTaskExecutorExtension : BeforeEachCallback, AfterEachCallback {
    
        override fun beforeEach(context: ExtensionContext?) {
            ArchTaskExecutor.getInstance().setDelegate(object : TaskExecutor() {
                override fun executeOnDiskIO(runnable: Runnable) {
                    runnable.run()
                }
    
                override fun postToMainThread(runnable: Runnable) {
                    runnable.run()
                }
    
                override fun isMainThread(): Boolean {
                    return true
                }
            })
        }
    
        override fun afterEach(context: ExtensionContext?) {
            ArchTaskExecutor.getInstance().setDelegate(null)
        }
    }
    

    Use this extension by annotating your test class like so:

    @ExtendWith(InstantTaskExecutorExtension::class)
    class MyTestClass { ... }
    

    If you're new to extensions (they replace JUnit 4 Rules), you can find additional documentation here: https://junit.org/junit5/docs/current/user-guide/#extensions