0Pricing
Kotlin Academy · Leçon

Tester StateFlow et SharedFlow avec Turbine

Écrivez des tests unitaires pour l’état fondé sur Flow avec la bibliothèque de test Turbine.

Tester StateFlow et SharedFlow avec Turbine est une leçon Kotlin Academy gratuite sur CoddyKit. Ceci est la leçon 4 sur 4. Tu peux lire la leçon complète ci-dessous gratuitement — puis la pratiquer en direct dans le navigateur avec un éditeur de code intégré et un tuteur IA 24/7. Elle fait partie du parcours d'apprentissage Kotlin Academy, et ta progression se synchronise sur le web et l'application CoddyKit. Le cours Kotlin Academy comprend 4 leçons au total.

Pourquoi Turbine ?

Turbine est une bibliothèque de tests pour Kotlin Flow qui facilite la vérification des émissions. Sans elle, tester les flux nécessite de collecter manuellement les canaux avec des délais d’expiration.

// Add to test dependencies:
// testImplementation("app.cash.turbine:turbine:1.0.0")
// testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.7.3")
import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun main() { println("Turbine + runTest = clean flow testing") }

Tester simplement un flux avec test()

flow.test { } collecte le flux et vous permet de vérifier chaque émission à l’aide de awaitItem(), awaitComplete() et awaitError().

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testBasicFlow() = runTest {
    val flow = flowOf(1, 2, 3)
    flow.test {
        assertEquals(1, awaitItem())
        assertEquals(2, awaitItem())
        assertEquals(3, awaitItem())
        awaitComplete()
    }
}

Tester StateFlow

StateFlow émet toujours sa valeur courante lors de la collecte. Le awaitItem() de Turbine capture cette émission initiale.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testStateFlow() = runTest {
    val state = MutableStateFlow("loading")
    state.test {
        assertEquals("loading", awaitItem())  // initial value
        state.value = "success"
        assertEquals("success", awaitItem())
        cancelAndIgnoreRemainingEvents()
    }
}

Tester les événements de SharedFlow

Un SharedFlow avec replay=0 n’émet que les événements futurs. Commencez par collecter, puis émettez l’événement.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testSharedFlow() = runTest {
    val events = MutableSharedFlow<String>()
    events.test {
        events.emit("LoginSuccess")
        assertEquals("LoginSuccess", awaitItem())
        events.emit("Navigate")
        assertEquals("Navigate", awaitItem())
        cancelAndIgnoreRemainingEvents()
    }
}

runTest et le temps virtuel

runTest utilise le temps virtuel : les appels à delay() font avancer instantanément le temps virtuel, ce qui accélère les tests comportant des délais.

import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
import app.cash.turbine.*
fun testDelayedFlow() = runTest {
    val flow = flow {
        delay(1000)  // virtual — runs instantly
        emit("done")
    }
    flow.test {
        assertEquals("done", awaitItem())
        awaitComplete()
    }
}

awaitError pour tester les exceptions

awaitError() vérifie que le flux se termine avec une exception. Utilisez-le avec le type d’exception attendu.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testFlowError() = runTest {
    val flow = flow {
        emit(1)
        throw RuntimeException("stream error")
    }
    flow.test {
        assertEquals(1, awaitItem())
        val error = awaitError()
        assertEquals("stream error", error.message)
    }
}

expectNoEvents et cancelAndIgnoreRemainingEvents

expectNoEvents() vérifie qu’aucune émission n’a eu lieu. cancelAndIgnoreRemainingEvents() annule la collecte en ignorant les émissions en attente.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testNoEvents() = runTest {
    val flow = MutableSharedFlow<Int>()
    flow.test {
        expectNoEvents()  // nothing emitted yet
        flow.emit(1)
        assertEquals(1, awaitItem())
        cancelAndIgnoreRemainingEvents()
    }
}

Tester un ViewModel avec Turbine

Testez le StateFlow d’un ViewModel en injectant un faux dépôt et en vérifiant les transitions d’état.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
sealed class State { object Loading : State(); data class Data(val v: String) : State() }
class FakeRepo { suspend fun fetch() = "result" }
class VM(repo: FakeRepo, scope: kotlinx.coroutines.CoroutineScope) {
    private val _s = MutableStateFlow<State>(State.Loading)
    val state = _s.asStateFlow()
    init { scope.launch { _s.value = State.Data(repo.fetch()) } }
}
fun testVM() = runTest {
    val vm = VM(FakeRepo(), backgroundScope)
    vm.state.test {
        assertEquals(State.Loading, awaitItem())
        val data = awaitItem() as State.Data
        assertEquals("result", data.v)
        cancelAndIgnoreRemainingEvents()
    }
}

Plusieurs collecteurs avec turbineScope

turbineScope { } vous permet de tester plusieurs flux simultanément, chacun avec sa propre instance de Turbine.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testMultipleFlows() = runTest {
    val a = MutableStateFlow(1)
    val b = MutableStateFlow("x")
    turbineScope {
        val aTurbine = a.testIn(backgroundScope)
        val bTurbine = b.testIn(backgroundScope)
        assertEquals(1, aTurbine.awaitItem())
        assertEquals("x", bTurbine.awaitItem())
        a.value = 2
        assertEquals(2, aTurbine.awaitItem())
        aTurbine.cancelAndIgnoreRemainingEvents()
        bTurbine.cancelAndIgnoreRemainingEvents()
    }
}

Tester les opérateurs de flux

Testez isolément le comportement des opérateurs de flux personnalisés et des transformations à l’aide de Turbine.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testOperators() = runTest {
    (1..5).asFlow()
        .filter { it % 2 == 0 }
        .map { it * 10 }
        .test {
            assertEquals(20, awaitItem())
            assertEquals(40, awaitItem())
            awaitComplete()
        }
}

Tester avec une avance temporelle simulée

Utilisez testScheduler.advanceTimeBy() pour contrôler précisément le temps virtuel dans les tests qui comportent delay.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testTimedFlow() = runTest {
    val flow = flow {
        emit("start")
        delay(500)
        emit("end")
    }
    flow.test {
        assertEquals("start", awaitItem())
        testScheduler.advanceTimeBy(500)
        assertEquals("end", awaitItem())
        awaitComplete()
    }
}

Erreurs courantes

Annulez toujours Turbine à la fin des tests de flux chauds. Oublier cancelAndIgnoreRemainingEvents() fait attendre les tests indéfiniment de nouveaux événements.

import app.cash.turbine.*
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.test.*
fun testGoodPractice() = runTest {
    val state = MutableStateFlow(0)
    state.test {
        awaitItem()  // initial 0
        state.value = 1
        awaitItem()  // 1
        // Always cancel hot flows:
        cancelAndIgnoreRemainingEvents()
    }
}

Vérification rapide

Quelle fonction de Turbine collecte l’élément émis suivant et lève une exception si le flux se termine ou rencontre une erreur avant cela ?

Récapitulatif

Turbine simplifie les tests de Flow avec awaitItem(), awaitComplete() et awaitError(). Utilisez runTest pour le temps virtuel. Annulez toujours les tests de flux chauds avec cancelAndIgnoreRemainingEvents().

Questions Fréquemment Posées

La leçon « Tester StateFlow et SharedFlow avec Turbine » est-elle gratuite ?

Oui — le texte complet de « Tester StateFlow et SharedFlow avec Turbine » est gratuit à lire ici sur le web. Pour la pratiquer de manière interactive (un éditeur de code intégré et un tuteur IA 24/7) et déverrouiller le reste du cours Kotlin Academy, passe à CoddyKit PRO. Le cours Kotlin Academy comprend 4 leçons au total.

Qu'est-ce que j'apprendrai dans « Tester StateFlow et SharedFlow avec Turbine » ?

Écrivez des tests unitaires pour l’état fondé sur Flow avec la bibliothèque de test Turbine. Tu pratiques Kotlin Academy avec du code pratique que tu exécutes directement dans le navigateur, et un tuteur IA 24/7 répond à tes questions au fur et à mesure que tu avances dans la leçon.

Dois-je avoir de l'expérience pour commencer Kotlin Academy ?

Aucune expérience préalable n'est requise. Kotlin Academy sur CoddyKit est structuré pour les débutants jusqu'aux apprenants avancés, donc tu peux commencer ici ou depuis le début et avancer à ton rythme. Ceci est la leçon 4 sur 4.

Combien de temps prend la leçon « Tester StateFlow et SharedFlow avec Turbine » ?

La plupart des leçons CoddyKit prennent environ 5–10 minutes. Chacune est courte et interactive, tu progresses régulièrement et tu repiques exactement où tu t'es arrêté sur le web et l'app.

Peux-tu écrire et exécuter du code dans cette leçon Kotlin Academy ?

Oui. Chaque leçon Kotlin Academy inclut un éditeur de code intégré, tu écris et exécutes du vrai code directement dans ton navigateur et tu reçois des retours IA instantanés — aucune configuration locale requise.

Toutes les leçons de ce cours

  1. StateFlow : conteneur d’état actif pour l’interface utilisateur
  2. SharedFlow : bus d’événements et événements ponctuels
  3. Transformer un Flow froid en flux actif avec shareIn et stateIn
  4. Tester StateFlow et SharedFlow avec Turbine
← Retour à Kotlin Academy