Android LiveData in androidTest returns null

做~自己de王妃 提交于 2021-01-04 03:23:28

问题


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.


回答1:


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




回答2:


If you are doing Kotlin, rather than Java, then you can also use:

import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer
import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit

// Original Java: https://github.com/googlesamples/android-architecture-components/blob/master/BasicSample/app/src/androidTest/java/com/example/android/persistence/LiveDataTestUtil.java

object 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.
     */
    @Throws(InterruptedException::class)
    fun <T> getValue(liveData: LiveData<T>): T? {
        val data = arrayOfNulls<Any>(1)
        val latch = CountDownLatch(1)
        val observer: Observer<T?> = object : Observer<T?> {
            override fun onChanged(o: T?) {
                data[0] = o
                latch.countDown()
                liveData.removeObserver(this)
            }
        }
        liveData.observeForever(observer)
        latch.await(2, TimeUnit.SECONDS)
        @Suppress("UNCHECKED_CAST")
        return data[0] as T?
    }
}

(At the moment the feature of A/S for automigration of Java to Kotlin doesn't quite work correctly for the Google class)



来源:https://stackoverflow.com/questions/48602979/android-livedata-in-androidtest-returns-null

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!