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.
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
.
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
.
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.