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

Coroutines-单元测试ViewModelScope.Launch方法

全兴运
2023-03-14

测试失败,因为结果。为什么runblocking{...}不以阻塞的方式在ViewModel中运行launch块?

我知道,如果将其转换为返回deferred对象的async方法,则可以通过调用await()获取对象,或者可以返回job并调用join()。但是,我想通过将我的ViewModel方法保留为void函数来实现这一点,有没有办法做到这一点呢?

// MyViewModel.kt

class MyViewModel(application: Application) : AndroidViewModel(application) {

    val logic = Logic()
    val myLiveData = MutableLiveData<Result>()

    fun doSomething() {
        viewModelScope.launch(MyDispatchers.Background) {
            System.out.println("Calling work")
            val result = logic.doWork()
            System.out.println("Got result")
            myLiveData.postValue(result)
            System.out.println("Posted result")
        }
    }

    private class Logic {
        suspend fun doWork(): Result? {
          return suspendCoroutine { cont ->
              Network.getResultAsync(object : Callback<Result> {
                      override fun onSuccess(result: Result) {
                          cont.resume(result)
                      }

                     override fun onError(error: Throwable) {
                          cont.resumeWithException(error)
                      }
                  })
          }
    }
}
// MyViewModelTest.kt

@RunWith(RobolectricTestRunner::class)
class MyViewModelTest {

    lateinit var viewModel: MyViewModel

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

    @Before
    fun init() {
        viewModel = MyViewModel(ApplicationProvider.getApplicationContext())
    }

    @Test
    fun testSomething() {
        runBlocking {
            System.out.println("Called doSomething")
            viewModel.doSomething()
        }
        System.out.println("Getting result value")
        val result = viewModel.myLiveData.value
        System.out.println("Result value : $result")
        assertNotNull(result) // Fails here
    }
}

共有1个答案

宰父淳
2023-03-14

您需要做的是用给定的Dispatcher将coroutine的启动打包到一个块中。

var ui: CoroutineDispatcher = Dispatchers.Main
var io: CoroutineDispatcher =  Dispatchers.IO
var background: CoroutineDispatcher = Dispatchers.Default

fun ViewModel.uiJob(block: suspend CoroutineScope.() -> Unit): Job {
    return viewModelScope.launch(ui) {
        block()
    }
}

fun ViewModel.ioJob(block: suspend CoroutineScope.() -> Unit): Job {
    return viewModelScope.launch(io) {
        block()
    }
}

fun ViewModel.backgroundJob(block: suspend CoroutineScope.() -> Unit): Job {
    return viewModelScope.launch(background) {
        block()
    }
}

注意顶部的ui、io和背景。这里的一切都是顶级+扩展功能。

然后,在viewModel中,您可以像这样开始您的coroutine:

uiJob {
    when (val result = fetchRubyContributorsUseCase.execute()) {
    // ... handle result of suspend fun execute() here         
}
@ExperimentalCoroutinesApi
private fun unconfinifyTestScope() {
    ui = Dispatchers.Unconfined
    io = Dispatchers.Unconfined
    background = Dispatchers.Unconfined
}
 类似资料:
  • 现在我想对这个函数进行单元测试。我正在使用Mockk: 不幸的是,由于以下异常,此测试失败: 我尝试用替换,但测试似乎在无限循环中等待。 有人能帮我弄一下这个吗? 提前致谢

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

  • 问题内容: 我有以前具有大量方法的类,因此我将此方法的工作细分为“辅助”方法。 这些辅助方法声明为强制执行封装- 但是我想对大型公共方法进行单元测试。是否也可以对辅助方法进行单元测试,好象其中的一个失败,而调用它的公共方法也会失败,这样我们就可以确定为什么失败了? 另外,为了使用模拟对象测试这些对象,我需要将其可见性从私有更改为受保护,这是否可取? 问题答案: 一种方法是省略测试并将其放在同一程序

  • Android Studio 1.1 添加了单元测试支持,详细请看 Unit testing support。本章的其余部分描述的是 “instrumentation tests”。利用 Instrumentation 测试框架可以构建独立的测试 APK 并运行在真实设备(或模拟器)中进行测试。

  • 英文原文:http://emberjs.com/guides/testing/unit/ 单元测试用于测试代码的一个小片段,确保其功能正常。与集成测试不同,单元测试被限定在一个范围内,并且不需要Ember应用运行。 全局 vs 模块 过去如果没有作为一个全局变量加载整个Ember应用,要对应用进行测试非常困难。通过使用模块(CommonJS,AMD等)来编写应用,可以只加载被测试的部分,而不用将其

  • 我在ViewModel中有一个函数有两个状态,第一个状态总是加载,第二个状态取决于api或db交互的结果。 这是函数 但是我找不到测试状态是否发生的方法,所以我修改了现有的扩展函数,以 若要在LiveData更改时将数据添加到列表中,并在该列表中存储状态,但它从不返回加载状态,因为它发生在observe启动之前。是否有方法测试的多个值?