Kotlin Academy · leksjon

Teste StateFlow og SharedFlow med Turbine

Skriv enhetstester for Flow-basert tilstand med testbiblioteket Turbine.

Leksjon 4 av 414 trinn

Teste StateFlow og SharedFlow med Turbine er en gratis leksjon i Kotlin Academy på CoddyKit. Dette er leksjon 4 av 4. Du kan lese valgfritt 3 leksjoner fra denne læringsstien gratis i sin helhet – deretter låser CoddyKit PRO opp alle leksjoner, samt praktisk øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Den er en del av læringsløpet i Kotlin Academy, og fremdriften din synkroniseres mellom nettet og CoddyKit-appen. Kurset i Kotlin Academy inneholder totalt 4 leksjoner.

Hvorfor Turbine?

Turbine er et testbibliotek for Kotlin Flow som gjør det enkelt å kontrollere verdier som sendes ut. Uten Turbine krever testing av flows manuell innsamling fra kanaler med tidsavbrudd.

// 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") }

Grunnleggende Flow-testing med test()

flow.test { } samler inn flow-en og lar Dem kontrollere hver verdi som sendes ut, ved hjelp av awaitItem(), awaitComplete() og 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()
    }
}

Testing av StateFlow

StateFlow sender alltid ut den gjeldende verdien når den samles inn. Turbines awaitItem() fanger opp denne første verdien.

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()
    }
}

Testing av SharedFlow-hendelser

SharedFlow med replay=0 sender bare ut fremtidige hendelser. Start innsamlingen først, og send deretter ut en hendelse.

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 for virtuell tid

runTest bruker virtuell tid – kall til delay() framskyver den virtuelle tiden umiddelbart, slik at tester med forsinkelser går raskt.

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 for testing av unntak

awaitError() kontrollerer at flow-en avsluttes med et unntak. Bruk den sammen med den forventede unntakstypen.

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 og cancelAndIgnoreRemainingEvents

expectNoEvents() kontrollerer at ingenting ble sendt ut. cancelAndIgnoreRemainingEvents() kansellerer innsamlingen og ignorerer ventende verdier som skal sendes ut.

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()
    }
}

Testing av ViewModel med Turbine

Test en ViewModels StateFlow ved å sette inn et falskt repository og kontrollere overgangene mellom tilstander.

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()
    }
}

Flere samlere med turbineScope

turbineScope { } lar Dem teste flere flows samtidig, hver med sin egen Turbine-instans.

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()
    }
}

Testing av Flow-operatorer

Test oppførselen til egendefinerte flow-operatorer og transformasjoner isolert ved hjelp av 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()
        }
}

Testing med kunstig tidsframskriving

Bruk testScheduler.advanceTimeBy() til å styre den virtuelle tiden nøyaktig i tester som bruker 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()
    }
}

Vanlige feil

Kanseller alltid Turbine på slutten av tester av varme flows. Hvis De glemmer cancelAndIgnoreRemainingEvents(), blir testene hengende mens de venter på flere hendelser.

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()
    }
}

Hurtigsjekk

Hvilken Turbine-funksjon samler inn det neste elementet som sendes ut, og kaster en feil hvis flow-en fullføres eller gir en feil først?

Oppsummering

Turbine forenkler testing av Flow med awaitItem(), awaitComplete() og awaitError(). Bruk runTest for virtuell tid. Kanseller alltid tester av varme flows med cancelAndIgnoreRemainingEvents().

Gratis å komme i gang

Lær deg Kotlin med en AI-veileder – gratis

Skriv og kjør ekte kode i nettleseren, få umiddelbar hjelp fra en AI-veileder som er tilgjengelig døgnet rundt, og fortsett der du slapp – på nettet eller i appen.

Kurs
51
Leksjoner
203

Ofte stilte spørsmål

Er leksjonen «Teste StateFlow og SharedFlow med Turbine» gratis?

Ja – du kan lese valgfritt 3 av leksjonene i læringsstien Kotlin Academy, inkludert «Teste StateFlow og SharedFlow med Turbine», gratis i sin helhet her på nettet. Deretter låser CoddyKit PRO opp alle leksjoner, samt interaktiv øving med en innebygd kodeeditor og en AI-veileder som er tilgjengelig døgnet rundt. Kurset i Kotlin Academy inneholder totalt 4 leksjoner.

Hva lærer jeg i «Teste StateFlow og SharedFlow med Turbine»?

Skriv enhetstester for Flow-basert tilstand med testbiblioteket Turbine. Du øver på Kotlin Academy med praktisk kode som du kjører direkte i nettleseren, mens en AI-veileder som er tilgjengelig døgnet rundt, svarer på spørsmålene dine mens du jobber deg gjennom leksjonen.

Trenger jeg erfaring for å begynne med Kotlin Academy?

Ingen tidligere erfaring er nødvendig. Kotlin Academy på CoddyKit er lagt opp for både nybegynnere og viderekomne, så De kan begynne her eller helt fra start og lære i Deres eget tempo. Dette er leksjon 4 av 4.

Hvor lang tid tar leksjonen «Teste StateFlow og SharedFlow med Turbine»?

De fleste CoddyKit-leksjoner tar omtrent 5–10 minutter. Hver leksjon er kort og interaktiv, slik at De gjør jevne fremskritt og kan fortsette akkurat der De slapp – både på nettet og i appen.

Kan jeg skrive og kjøre kode i denne Kotlin Academy-leksjonen?

Ja. Alle Kotlin Academy-leksjoner har en innebygd kodeeditor, slik at De kan skrive og kjøre ekte kode direkte i nettleseren og få umiddelbar tilbakemelding fra AI – uten lokal konfigurering.

Alle leksjonene i dette kurset

  1. StateFlow: Varm tilstandsbeholder for UI
  2. SharedFlow: Hendelsesbusser og engangshendelser
  3. Konvertere kald Flow til varm med shareIn og stateIn
  4. Teste StateFlow og SharedFlow med Turbine
← Tilbake til Kotlin Academy