简体   繁体   English

测试协程和 LiveData:协程结果未反映在 LiveData 上

[英]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

ViewModelTest视图模型测试

@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)
    }
}

TasksViewModel任务视图模型

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.下面列出了帮助程序类,我只是从示例项目中复制了相同的类。

LiveDataTestUtil LiveDataTestUtil

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
    }
}

MainCoroutineRule MainCoroutineRule

@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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM