Тестирование Dispatcher IO и ViewModelScope в модульных тестах

Я пытаюсь протестировать некоторый код в своей модели представления. Я пытаюсь сделать два сетевых вызова с асинхронным диспетчером, но не понял, как это проверить. Это упрощенный пример того, что я по сути пытаюсь сделать:

// 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 будет выполнен первым надежным образом?

Это зависит от ваших требований к экземпляру. Рассмотрите возможность использования supervisorScope, joinAll или advanceUntilIdle. Проверьте их реализацию и поймите, какой из них подходит вам лучше всего.

Bob 17.06.2024 12:17
1
1
125
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Попробуйте заменить runCurrent() на advanceUntilIdle().

По сути, он будет запускать все остальные сопрограммы в планировщике до тех пор, пока в очереди ничего не останется. Это хороший выбор по умолчанию, позволяющий запускать все ожидающие сопрограммы, и он будет работать в большинстве тестовых сценариев. Об этом говорится в документации Google. Я нашел это здесь https://developer.android.com/kotlin/coroutines/test

Ответ принят как подходящий

Вам необходимо использовать внедрение зависимостей, чтобы предоставить диспетчер ввода-вывода вашему ViewModel, чтобы сделать его тестируемым. Тогда вы можете использовать внедренный диспетчер вместо жесткого кодирования Dispatchers.IO:

class ViewModel(
    //...
    private val ioDispatcher: CoroutineDispatcher = Dispatchers.IO,
) {
//...
fun initialise() {
    viewModelScope.launch(coroutineExceptionHandler) {
        val accountDeferred = async(ioDispatcher) { accountModel.getAccounts() }
        val contentDeferred = async(ioDispatcher) { contentModel.getContent() }

Из документации по тестированию сопрограмм:

Если основной диспетчер был заменен на TestDispatcher, все вновь созданные TestDispatcher будут автоматически использовать планировщик из главного диспетчера, включая StandardTestDispatcher, созданный runTest, если ему не передан другой диспетчер.

Это упрощает обеспечение использования только одного планировщика во время теста. Чтобы это работало, обязательно создайте все остальные экземпляры TestDispatcher после вызова Dispatchers.setMain.

Предполагая, что CoroutineTestRule установил диспетчер Main, теперь вы можете создать новый TestDispatcher для внедрения, и он будет использовать тот же планировщик:

@OptIn(ExperimentalCoroutinesApi::class)
@Test
fun `This is the test`() = runTest {
    val dispatcher = StandardTestDispatcher()
    val viewmodel = ViewModel(/*other args*/, dispatcher)
    advanceUntilIdle()
    //...
}

Спасибо, это то, что я в итоге сделал, но делегировал ввод-вывод на уровень бизнес-уровня.

keyboard_user 23.06.2024 07:45

Другие вопросы по теме