[英]Testing Coroutines and LiveData: Coroutine result not reflected on LiveData
I am new to testing and I wanted to learn how to test Coroutines with MVVM pattern.我是测试新手,我想学习如何使用 MVVM 模式测试协程。 I just followed https://github.com/android/architecture-samples project and did a few changes (removed remote source).
我刚刚关注了 https://github.com/android/architecture-samples 项目并做了一些更改(删除了远程源)。 But when testing the ViewModel for fetching data from a repository, it keeps on failing with this error.
但是在测试 ViewModel 从存储库中获取数据时,它会一直失败并出现此错误。
value of : iterable.size()
expected : 3
but was : 0
iterable was: []
Expected :3
Actual :0
Below is my test class for the ViewModel
don't know what I'm missing.下面是我对
ViewModel
的测试 class 不知道我缺少什么。 Also when mocking the repository I can get the expected results from it when printing taskRepository.getTasks()
it just doesn't reflect on the LiveData
when calling loadTasks()
此外,当 mocking 存储库时,我可以在打印
taskRepository.getTasks()
时从中获得预期的结果,它只是在调用loadTasks()
时不会反映在LiveData
@ExperimentalCoroutinesApi
@RunWith(MockitoJUnitRunner::class)
class TasksViewModelTest {
private lateinit var tasksViewModel: TasksViewModel
val tasksRepository = mock(TasksRepository::class.java)
@ExperimentalCoroutinesApi
@get:Rule
var mainCoroutineRule = TestMainCoroutineRule()
// Executes each task synchronously using Architecture Components.
@get:Rule
var instantExecutorRule = InstantTaskExecutorRule()
@Before
fun setupViewModel() {
tasksViewModel = TasksViewModel(tasksRepository)
}
@Test
fun whenLoading_hasListOfTasks() = runBlockingTest {
val task1 = Task("title1", "description1")
val task2 = Task("title2", "description2")
val task3 = Task("title3", "description3")
`when`(tasksRepository.getTasks()).thenReturn(Result.Success(listOf(
task1,
task2,
task3
)))
tasksViewModel.loadTasks()
val tasks = LiveDataTestUtil.getValue(tasksViewModel.tasks)
assertThat(tasks).hasSize(3)
}
}
class TasksViewModel @Inject constructor(
private val repository: TasksRepository
) : ViewModel() {
private val _tasks = MutableLiveData<List<Task>>().apply { value = emptyList() }
val tasks: LiveData<List<Task>> = _tasks
fun loadTasks() {
viewModelScope.launch {
val tasksResult = repository.getTasks()
if (tasksResult is Success) {
val tasks = tasksResult.data
_tasks.value = ArrayList(tasks)
}
}
}
}
Helper classes are listed below, I just copied the same classes from the sample project.下面列出了帮助程序类,我只是从示例项目中复制了相同的类。
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.
*/
fun <T> getValue(liveData: LiveData<T>): T {
val data = arrayOfNulls<Any>(1)
val latch = CountDownLatch(1)
val observer = 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
}
}
@ExperimentalCoroutinesApi
class TestMainCoroutineRule : TestWatcher(), TestCoroutineScope by TestCoroutineScope() {
override fun starting(description: Description?) {
super.starting(description)
Dispatchers.setMain(this.coroutineContext[ContinuationInterceptor] as CoroutineDispatcher)
}
override fun finished(description: Description?) {
super.finished(description)
Dispatchers.resetMain()
}
}
Turns out it was a problem with mockito, I have an older version, and I found there's a library called mockito-kotlin
to simplify testing coroutines as stated here .原来这是 mockito 的问题,我有一个旧版本,我发现有一个名为
mockito-kotlin
的库来简化测试协程,如此处所述。 I then chaged my code to this and It's working well.然后我把我的代码改成了这个,它运行良好。
tasksRepository.stub {
onBlocking { getTasks() }.doReturn(Result.Success(listOf(task1, task2, task3)))
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.