首页 > 解决方案 > 单元测试 LiveData android.os.Looper 未模拟

问题描述

我正在尝试对我的 ViewModel 进行单元测试,该 ViewModel 通过将一些内容发布回活动,LiveData但是当我运行设置 livedata 值的方法时,我得到了错误

java.lang.RuntimeException:android.os.Looper 中的方法 getMainLooper 未模拟。

我已经阅读了几篇文章和文章,您所要做的就是添加一个RuleforInstantTaskExecutorRule并且它应该可以正常工作,但我这样做了,但我仍然得到错误。

这是我的单元测试

val observer: Observer<String> = mock(Observer::class.java) as Observer<String>

@get:Rule
var rule: TestRule = InstantTaskExecutorRule()

@Test
fun testSearchDataValidationFailureMissingSearchLocation() {
    val viewModel = MoveViewModel()

    val param1 = 0
    val param2 = 1
    val param3 = "1234"
    viewModel.dialogMessageLiveData.observeForever(observer)

    Assert.assertFalse(viewModel.validateSearchData(param1, param2, param3))

    verify(observer).onChanged("Data not valid")
}

这是我正在尝试测试的方法

fun validateSearchData(param1: Int, param2: Int, param3: String): Boolean {
    var valid: Boolean = false

    if (param1 == 0 || param2 == 0 || param3.isBlank()) {
        dialogMessageLiveData.postValue("Data not valid")
    } else {
        valid = true
    }

    return valid
}

我不确定还有什么办法可以解决这个问题,有人可以提出其他解决方案吗?

标签: androidunit-testingmockitoandroid-architecture-componentsandroid-livedata

解决方案


您可以使用此扩展来获取 [LiveData] 的值或等待它具有一个超时。在主机端 (JVM) 测试中使用此扩展。建议与它一起使用InstantTaskExecutorRule或类似的机制来同步执行任务。

@VisibleForTesting(otherwise = VisibleForTesting.NONE)
fun <T> LiveData<T>.getOrAwaitValue(
    time: Long = 2,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): T {
    var data: T? = null
    val latch = CountDownLatch(1)
    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data = o
            latch.countDown()
            this@getOrAwaitValue.removeObserver(this)
        }
    }
    this.observeForever(observer)

    try {
        afterObserve.invoke()

        // Don't wait indefinitely if the LiveData is not set.
        if (!latch.await(time, timeUnit)) {
            throw TimeoutException("LiveData value was never set.")
        }

    } finally {
        this.removeObserver(observer)
    }

    @Suppress("UNCHECKED_CAST")
    return data as T
}

然后在您的测试用例中,您可以获得值

viewModel.dialogMessageLiveData.getOrAwaitValue() 

推荐阅读