Как выполнить блок-тест сопрограммы, если он содержит задержку сопрограммы? - PullRequest
0 голосов
/ 13 ноября 2018

Когда я добавлю сопрограмму delay () в мою модель представления, оставшаяся часть кода не будет выполнена.

Это мой демонстрационный код:

class SimpleViewModel : ViewModel(), CoroutineScope {

    override val coroutineContext: CoroutineContext
        get() = Dispatchers.Unconfined

    var data = 0

    fun doSomething() {
        launch {
            delay(1000)
            data = 1
        }
    }
}

class ScopedViewModelTest {

    @Test
    fun coroutineDelay() {
        // Arrange
        val viewModel = SimpleViewModel()

        // ActTes
        viewModel.doSomething()

        // Assert
        Assert.assertEquals(1, viewModel.data)
    }
}

Я получил результат утверждения:

java.lang.AssertionError: 
Expected :1
Actual   :0

Есть идеи, как это исправить?

Ответы [ 2 ]

0 голосов
/ 16 ноября 2018

Первая проблема в вашем коде заключается в том, что SimpleViewModel.coroutineContext не имеет Job, связанной с ним. Весь смысл превращения вашей модели представления в CoroutineScope заключается в возможности централизовать отмену всех сопрограмм, которые она запускает. Поэтому добавьте работу следующим образом (обратите внимание на отсутствие пользовательского метода получения):

class SimpleViewModel : ViewModel(), CoroutineScope {

    override val coroutineContext = Job() + Dispatchers.Unconfined

    var data = 0

    fun doSomething() {
        launch {
            delay(1000)
            data = 1
        }
    }
}

Теперь ваш тестовый код может гарантировать, что он переходит к утверждениям только после выполнения всех заданий, запущенных вашей моделью представления:

class ScopedViewModelTest {

    @Test
    fun coroutineDelay() {
        // Arrange
        val viewModel = SimpleViewModel()

        // ActTes
        viewModel.doSomething()

        // Assert
        runBlocking {
            viewModel.coroutineContext[Job]!!.children.forEach { it.join() }
        }
        Assert.assertEquals(1, viewModel.data)
    }
}
0 голосов
/ 13 ноября 2018

Вы запускаете сопрограмму, которая приостанавливается на 1 секунду перед установкой data на 1. Ваш тест просто вызывает doSomething, но не ждет, пока data фактически не будет установлен.Если вы добавите еще один, более длинный delay, к тесту он будет работать:

@Test     
fun coroutineDelay() = runBlocking {
    ...
    viewModel.doSomething()
    delay(1100)
    ...
}

Вы также можете сделать так, чтобы сопрограмма возвращала Deferred, который можно подождать:

fun doSomething(): Deferred<Unit> {
    return async {
        delay(1000)
        data = 1
    }
}

С await больше нет необходимости откладывать ваш код:

val model = SimpleViewModel()
model.doSomething().await()
...