在单元测试中测试 Dispatcher IO 和 ViewModelScope

问题描述 投票:0回答:1

我正在尝试在我的视图模型中测试一些代码。我正在尝试使用调度程序异步进行两个网络调用,但尚未弄清楚如何测试。这是我本质上想做的事情的简化示例,

// View Model Function

fun initialise() {
    viewModelScope.launch(coroutineExceptionHandler) {
        val accountDeferred = async(Dispatchers.IO) { accountModel.getAccounts() }
        val contentDeferred = async(Dispatchers.IO) { contentModel.getContent() }
        val account = accountDeferred.await()
        val content = contentDeferred.await()

        handleResult(account, content) // viewState is updated
    }
}

// Unit Test
// Simplified

class Test {
    @get:Rule
    val coroutineRule = CoroutineTestRule(StandardTestDispatcher())

    @Test
    fun `This is the test`() {
        runTest{
            whenever(accountModel.getAccounts()).thenReturn(
                Result.success( getAccountContent() , ReasonStatus.empty()))
            whenever(contentModel.getContent()).thenReturn(
                Result.success( getContent(), ReasonStatus.empty()))

            // start view model
            viewmodel = ViewModel(accountModel, contentModel)
            runCurrent()

            // In debugging mode, the variable viewState is set, before the two jobs in the viewmodel finish

            val viewState = viewmodel.viewState.value

            // check contents
            assertNotEmpty(viewState.accounts)

            // always fails since the network calls have not completed
        }
    }
}

有什么建议可以确保两个异步等待在 runTest 块中的其他代码首先以可靠的方式执行之前执行?

android kotlin unit-testing junit kotlin-coroutines
1个回答
0
投票

为了确保网络调用在单元测试中的断言之前完成,您可以使用 TestCoroutineScope 中的 advanceUntilIdle() 方法。

@ExperimentalCoroutinesApi
class ViewModelTest {

    @get:Rule
    val coroutineRule = CoroutineTestRule(StandardTestDispatcher())

    private val accountModel: AccountModel = mock(AccountModel::class.java)
    private val contentModel: ContentModel = mock(ContentModel::class.java)
    private lateinit var viewModel: ViewModel

    @Test
    fun `This is the test`() = runTest {
        whenever(accountModel.getAccounts()).thenReturn(Result.success(getAccountContent(), ReasonStatus.empty()))
        whenever(contentModel.getContent()).thenReturn(Result.success(getContent(), ReasonStatus.empty()))

        // Start view model
        viewModel = ViewModel(accountModel, contentModel)

        // This will run all pending tasks until they're idle
        advanceUntilIdle()

        // Now, viewState should be updated
        val viewState = viewModel.viewState.value

        // Check contents
        assertNotNull(viewState?.accounts)
        assertTrue(viewState?.accounts?.isNotEmpty() == true)
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.