对使用 ViewModelScope.launch 延迟调用挂起函数的 ViewModel 方法进行单元测试

sun*_*son 6 android unit-testing kotlin-coroutines

在我的文件中,ViewModel我有一个onTextChanged通过EditText. 在那里我用来viewModelScope.launch{}启动一个suspend函数。在该函数中我延迟了 500 毫秒。现在我该如何测试该onTextChanged方法?我尝试的所有操作总是导致测试在延迟完成之前完成。我尝试使用runBlockingTest,TestCoroutineDispatcherrunBlocking

文本观察器:

override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {
        searchJob = if (searchJob?.isActive == true) {
            searchJob?.cancel()
            viewModelScope.launch { search(s.toString()) }
        } else {
            viewModelScope.launch { search(s.toString()) }
        }
    }
Run Code Online (Sandbox Code Playgroud)

搜索方法:

 private suspend fun search(query: String) {
            delay(500)
            searchUseCase(SearchParams(query)).fold({

            }, {

            })
    }
Run Code Online (Sandbox Code Playgroud)

考试:

    @Test
    fun `Search is fired after 500ms when text is changed`()  = runBlockingTest {
        val viewModel = ViewModel(useCase)
        viewModel.onTextChanged("test", 0, 0, 0)

        //TODO assert time was 500ms or more

        //This fails
        coVerify(exactly = 1) { useCase.invoke(any()) }
    }
Run Code Online (Sandbox Code Playgroud)

Pet*_*ter 2

谷歌建议,您应该以某种方式注入CoroutineDispatcher您的视图模型,以便您可以在测试期间更改它。

class MainViewModel(
    private val dispatcher: CoroutineDispatcher = Dispatchers.Default
) : ViewModel() {

    private var _userData: MutableLiveData<Any> = MutableLiveData<Any>()
    val userData: LiveData<Any> = _userData

    fun savedDelayed() = viewModelScope.launch {
        delay(1000)
        saveSessionData()
    }

    suspend fun saveSessionData() {
        viewModelScope.launch(dispatcher) {
            _userData.value = "some_user_data"
        }
    }
}

@ExperimentalCoroutinesApi
class MainViewModelTest {

    private val testDispatcher = TestCoroutineDispatcher()

    @ExperimentalCoroutinesApi
    @get:Rule
    var mainCoroutineRule = MainCoroutineRule()

    @get:Rule
    var instantExecutorRule = InstantTaskExecutorRule()

    @Test
    fun testsSaveSessionData() = runBlockingTest {
        val mainViewModel = MainViewModel(testDispatcher)
      
        mainViewModel.savedDelayed()
        testDispatcher.advanceUntilIdle()
      
        val userData = mainViewModel.userData.value
        assertEquals("some_user_data", userData)
    }

}
Run Code Online (Sandbox Code Playgroud)

代码粘贴自中等文章,并根据您的用例进行了修改。请阅读文章以获取进一步说明: https://medium.com/swlh/unit-testing-with-kotlin-coroutines-the-android-way-19289838d257