【问题标题】:Android LiveData in androidTest returns nullandroidTest 中的 Android LiveData 返回 null
【发布时间】:2018-02-03 23:03:39
【问题描述】:

我正在运行一个 androidTest 仪器测试,并且我有一个使用 Room 从 DAO 对象返回 LiveData 的方法。

我这样调用方法:

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

我使用 Mockito 来模拟观察者:

@Mock
private lateinit var mMockObserver = Observer<Animal>

这应该返回一个 LiveData 实例,其中包含 id 为 1 的 Animal,但它为 null。我的理解是,为了让 LiveData 返回任何东西,必须有一个观察者。我是不是设置错了?

注意:如果我在 DAO 中更改 getAnimal() 的签名以直接返回 Animal,而不是 LiveData,那么它可以工作,所以我知道它与 LiveData 相关。

【问题讨论】:

  • AndroidTests(AndroidTest 文件夹中的测试)不是单元测试,而是插桩测试。此外,您错误地使用了 Mock 注释,您正在立即实例化 Observer ,这将(我不知道)a。使 Mock 注释无用或 b。使实例化无用。
  • 我在问题中的错误措辞已更新。我最近正在学习如何使用 Mockito,所以我似乎误解了如何使用它。看起来我还有一些阅读要做:) 我也不相信 Mockito 是我在这里需要的。我之前翻遍了 Google 的示例,看看他们是如何使用 LiveData 进行测试的,但我的答案错过了文件……现在用我的发现更新……

标签: android kotlin android-room android-architecture-components android-livedata


【解决方案1】:

经过一番挖掘后,我发现了 Google 通过其 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];
    }
}

这允许您传递 LiveData 实例并取回它所保存的值。

更新(JUnit 4):

您还可以使用InstantTaskExecutorRule 结合observeForever 来测试您的LiveData。在 Kotlin 中,您在测试类的顶部设置 @get:Rule val instantTaskExecutorRule = InstantTaskExecutorRule() 以确保同步处理 LiveData,然后在测试用例中设置 myLiveData.observeForever { /* Do something when event emitted */ } 以获取 LiveData 值。

更新(JUnit 5)

如果您使用的是 JUnit5,那么您可以使用此扩展来代替上面 Update (JUnit4) 中解释的规则。

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)
    }
}

通过像这样注释您的测试类来使用此扩展:

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

如果您不熟悉扩展(它们取代了 JUnit 4 规则),您可以在此处找到其他文档:https://junit.org/junit5/docs/current/user-guide/#extensions

【讨论】:

  • 链接失效,能否更新链接或提供解决方案?
  • 更新了断开的链接
  • 我所有涉及 LiveData 的“更高级别/集成”测试现在都基于这个答案,而且效果非常好。 InstantTaskExecutorRule 加上这个代码 sn-p 就是这样。非常感谢!
【解决方案2】:

如果你在做 Kotlin,而不是 Java,那么你也可以使用:

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?
    }
}

(目前,用于将 Java 自动迁移到 Kotlin 的 A/S 功能对于 Google 类并不完全正确)

【讨论】:

    猜你喜欢
    • 2019-02-07
    • 2021-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-25
    • 1970-01-01
    • 2012-06-22
    相关资源
    最近更新 更多