当前位置: 首页 > 知识库问答 >
问题:

ViewModel单元使用LiveData、Coroutines和MockK测试多个视图状态

寿嘉悦
2023-03-14

我在ViewModel中有一个函数有两个状态,第一个状态总是加载,第二个状态取决于api或db交互的结果。

这是函数

fun getPostWithSuspend() {

    myCoroutineScope.launch {

        // Set current state to LOADING
        _postStateWithSuspend.value = ViewState(LOADING)

        val result = postsUseCase.getPosts()

        // Check and assign result to UI
        val resultViewState = if (result.status == SUCCESS) {
            ViewState(SUCCESS, data = result.data?.get(0)?.title)
        } else {
            ViewState(ERROR, error = result.error)
        }

        _postStateWithSuspend.value = resultViewState
    }
}
   @Test
    fun `Given DataResult Error returned from useCase, should result error`() =
        testCoroutineRule.runBlockingTest {

            // GIVEN
            coEvery {
                useCase.getPosts()
            } returns DataResult.Error(Exception("Network error occurred."))

            // WHEN
            viewModel.getPostWithSuspend()

            // THEN
            val expected = viewModel.postStateWithSuspend.getOrAwaitMultipleValues(dataCount = 2)

//            Truth.assertThat("Network error occurred.").isEqualTo(expected?.error?.message)
//            Truth.assertThat(expected?.error).isInstanceOf(Exception::class.java)
            coVerify(atMost = 1) { useCase.getPosts() }
        }

但是我找不到测试加载状态是否发生的方法,所以我修改了现有的扩展函数,以

fun <T> LiveData<T>.getOrAwaitMultipleValues(
    time: Long = 2,
    dataCount: Int = 1,
    timeUnit: TimeUnit = TimeUnit.SECONDS,
    afterObserve: () -> Unit = {}
): List<T?> {

    val data = mutableListOf<T?>()
    val latch = CountDownLatch(dataCount)

    val observer = object : Observer<T> {
        override fun onChanged(o: T?) {
            data.add(o)
            latch.countDown()
            this@getOrAwaitMultipleValues.removeObserver(this)
        }
    }
    this.observeForever(observer)

    afterObserve.invoke()

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

    @Suppress("UNCHECKED_CAST")
    return data.toList()
}

若要在LiveData更改时将数据添加到列表中,并在该列表中存储状态,但它从不返回加载状态,因为它发生在observe启动之前。是否有方法测试livedata的多个值?

共有1个答案

陈飞
2023-03-14

使用mockk可以捕获这些值并将其存储在列表中,然后按顺序检查这些值。

    //create mockk object
    val observer = mockk<Observer<AnyObject>>()

    //create slot
    val slot = slot<AnyObject>()

    //create list to store values
    val list = arrayListOf<AnyObject>()

    //start observing
    viewModel.postStateWithSuspend.observeForever(observer)


    //capture value on every call
    every { observer.onChanged(capture(slot)) } answers {

        //store captured value
        list.add(slot.captured)
    }

    viewModel.getPostWithSuspend()
    
    //assert your values here
    
 类似资料:
  • 我的实现出了什么问题?谢谢

  • 测试失败,因为为。为什么不以阻塞的方式在ViewModel中运行块? 我知道,如果将其转换为返回对象的方法,则可以通过调用获取对象,或者可以返回并调用。但是,我想通过将我的ViewModel方法保留为函数来实现这一点,有没有办法做到这一点呢?

  • 如何使用Kotlin、Coroutines、ViewModel和LiveData处理查询方法的返回类型room 建筑失败了,我得到了很多错误,这些错误指向我的道类,错误是 错误1: 不确定如何处理查询方法的返回类型(java.lang.Object)。DELETE查询方法必须返回void或int(已删除的行数)。 错误2: 错误:查询方法参数应该是可以转换为数据库列的类型,或者是包含此类类型的列表

  • 我想对LiveData的结果序列进行单元测试。我有一个LiveData,它在加载、成功或错误时发出值。我想对其进行测试,以确保首先发出加载值,然后是成功或错误值。 有办法做到这一点吗?

  • 当将CoroutineScope注入到用于单元测试的ViewModel中时,是否也应该使用注入和定义CoroutineDispatcher,即使在生产代码中不需要它? 在此用例中,生产代码中不需要,因为在someRepository.kt中,reverfit处理上的线程,而返回上的数据,这两种情况都是默认的。 对保存在Kotlin流值中的Android的ViewModel视图状态值运行单元测试。