web-dev-qa-db-fra.com

Unité testant une coroutine Kotlin avec retard

J'essaie de tester unitaire une coroutine Kotlin qui utilise delay(). Pour le test unitaire, je ne me soucie pas de la delay(), cela ralentit simplement le test. Je voudrais exécuter le test d'une manière qui ne retarde pas réellement l'appel de delay().

J'ai essayé d'exécuter la coroutine en utilisant un contexte personnalisé qui délègue à CommonPool:

class TestUiContext : CoroutineDispatcher(), Delay {
    suspend override fun delay(time: Long, unit: TimeUnit) {
        // I'd like it to call this
    }

    override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
        // but instead it calls this
    }

    override fun dispatch(context: CoroutineContext, block: Runnable) {
        CommonPool.dispatch(context, block)
    }
}

J'espérais pouvoir revenir de la méthode delay() de mon contexte, mais à la place, il appelle ma méthode scheduleResumeAfterDelay(), et je ne sais pas comment déléguer cela au planificateur par défaut.

29
Erik Browne

Dans kotlinx.coroutines v1.2.1, ils ont ajouté le module kotlinx-coroutines-test . Il inclut le générateur de coroutine runBlockingTest, ainsi qu'un TestCoroutineScope et TestCoroutineDispatcher. Ils permettent d'avancer automatiquement le temps, ainsi que de contrôler explicitement le temps de test des coroutines avec delay.

0
Erik Browne

Si vous ne voulez pas de retard, pourquoi ne reprenez-vous pas simplement la suite de l'appel planifié?:

class TestUiContext : CoroutineDispatcher(), Delay {
    override fun scheduleResumeAfterDelay(time: Long, unit: TimeUnit, continuation: CancellableContinuation<Unit>) {
        continuation.resume(Unit)
    }

    override fun dispatch(context: CoroutineContext, block: Runnable) {
        //CommonPool.dispatch(context, block)  // dispatch on CommonPool
        block.run()  // dispatch on calling thread
    }
}

De cette façon, delay() reprendra sans délai. Notez que cela suspend toujours à retard, donc d'autres coroutines peuvent toujours s'exécuter (comme yield())

@Test
fun `test with delay`() {
    runBlocking(TestUiContext()) {
        launch { println("launched") }
        println("start")
        delay(5000)
        println("stop")
    }
}

Fonctionne sans délai et imprime:

start
launched
stop

MODIFIER:

Vous pouvez contrôler où la continuation est exécutée en personnalisant la fonction dispatch.

18
bj0

Dans kotlinx.coroutines v0.23.0, ils ont introduit un TestCoroutineContext .

Pro: il permet vraiment de tester des coroutines avec delay. Vous pouvez définir l'horloge virtuelle du CoroutineContext à un moment précis et vérifier le comportement attendu.

Con: si votre code coroutine n'utilise pas delay, et que vous voulez juste qu'il s'exécute de manière synchrone sur le thread appelant, il est légèrement plus lourd à utiliser que le TestUiContext de la réponse de @ bj0 ( vous devez appeler triggerActions() sur le TestCoroutineContext pour obtenir l'exécution de la coroutine).

Sidenote: Le TestCoroutineContext vit maintenant dans le kotlinx-coroutines-test module commençant par coroutines version 1.2.1, et sera marqué comme obsolète ou inexistant dans la bibliothèque coroutine standard dans les versions supérieures à cette version.

3
Erik Browne