如何测试在断言之前阻塞线程的视图模型协同程序



我正试图为视图模型编写一个测试。我正在做一个仪器测试,因为我需要context

视图模型和测试看起来像:

class MyViewModel(
private val dispatcher: CoroutineDispatchers = Dispatchers.IO) : ViewModel() {
private val _livedata = MutableLiveData<Boolean>()
val livedata: LiveData<Boolean> = _livedata
fun doSomething() {
viewModelScope.launch(dispatcher) {
//suspend function with retrofit
_livedata.value = true
}
}
}
class MyViewModelTest {
private lateinit var viewModel: MyViewModel
@get:Rule
var mainCoroutineRule = MainCoroutineRule()
@get:Rule
var instantTaskExecutorRule = InstantTaskExecutorRule()
@Before
fun setup() {
viewModel = MyViewModel(mainCoroutineRule.dispatcher)
}
@Test
fun testMyViewModel() {
mainCoroutineRule.runBlockingTest {
viewModel.doSomething()
mainCoroutineRule.dispatcher.advanceUntilIdle()
val result = viewModel.livedata.getOrAwaitValue()
assertThat(result).isTrue()
}
}
}

问题是,由于doSomething()在另一个协程上被调用,并且是异步完成的,因此result是空的。

我如何运行我的测试,以便挂起函数阻塞线程,以便我的断言在挂起函数完成后捕获result

从外面的信息来看,我很困惑。

我认为我不需要InstantTaskExecutorRule(),因为我正在进行仪器测试?

添加此规则没有帮助:

@ExperimentalCoroutinesApi
class MainCoroutineRule(val dispatcher: TestCoroutineDispatcher = TestCoroutineDispatcher()):
TestWatcher(),
TestCoroutineScope by TestCoroutineScope(dispatcher) {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(dispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
cleanupTestCoroutines()
Dispatchers.resetMain()
}
}

我需要在视图模型中注入一个阻塞主线程的协程调度器吗?

为了解决这个问题,我不得不使用https://medium.com/androiddevelopers/unit-testing-livedata-and-other-common-observability-problems-bb477262eb04

/* Copyright 2019 Google LLC.   
SPDX-License-Identifier: Apache-2.0 */
fun <T> LiveData<T>.getOrAwaitValue(
time: Long = 2,
timeUnit: TimeUnit = TimeUnit.SECONDS
): 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)
// Don't wait indefinitely if the LiveData is not set.
if (!latch.await(time, timeUnit)) {
throw TimeoutException("LiveData value was never set.")
}
@Suppress("UNCHECKED_CAST")
return data as T
}

如果您要启动协同程序来调用改装服务,您可能需要在IO调度器上执行此操作。您可以将其作为依赖项传入,然后在测试中通过一个测试来控制它

class MyViewModel : ViewModel(private val ioDispatcher: Dispatcher = Dispatchers.IO) {
private val _livedata = MutableLiveData<Boolean>()
val livedata: LiveData<Boolean> = _livedata
fun doSomething() {
viewModelScope.launch(ioDispatcher) {
//suspend function with retrofit
_livedata.value = true
}
}
}

测试:

@Before
fun setup() {
viewModel = MyViewModel(coroutineRule.dispatcher)
}
@Test
fun testMyViewModel() {
mainCoroutineRule.runBlockingTest {
viewModel.doSomething()
coroutineRule.dispatcher.advanceUntilIdle()
val result = viewModel.livedata.value
assertThat(result).isTrue()
}
}

最新更新