Kotlin Academy · Oppitunti

StateFlow’n ja SharedFlow’n testaaminen Turbinella

Kirjoittakaa Flow-pohjaisen tilan yksikkötestejä Turbine-testauskirjastolla.

Oppitunti 4/414 vaihetta

StateFlow’n ja SharedFlow’n testaaminen Turbinella on ilmainen Kotlin Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea tästä oppimispolusta kokonaan mitkä tahansa 3 oppituntia ilmaiseksi — sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä käytännön harjoittelun sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Oppitunti kuuluu Kotlin Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Miksi Turbine?

Turbine on Kotlinin Flow-testikirjasto, joka helpottaa emissioiden tarkistamista. Ilman sitä Flow-virtojen testaaminen edellyttää manuaalista kanavan keräämistä aikakatkaisuineen.

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

Flow-virran perustestaus test()-funktiolla

flow.test { } kerää Flow-virran ja mahdollistaa jokaisen emission tarkistamisen funktioilla awaitItem(), awaitComplete() ja 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()
    }
}

StateFlow'n testaaminen

StateFlow lähettää nykyisen arvonsa aina, kun sitä aletaan kerätä. Turbinen awaitItem() tallentaa tämän ensimmäisen emission.

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

SharedFlow-tapahtumien testaaminen

SharedFlow, jonka asetuksena on replay=0, lähettää vain tulevat tapahtumat. Aloittakaa kerääminen ensin ja lähettäkää tapahtuma vasta sen jälkeen.

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 ja virtuaaliaika

runTest käyttää virtuaaliaikaa — delay()-kutsut siirtävät virtuaaliaikaa välittömästi, joten viiveitä sisältävät testit suoritetaan nopeasti.

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 poikkeusten testaamiseen

awaitError() tarkistaa, että Flow-virta päättyy poikkeukseen. Käyttäkää sitä odotetun poikkeustyypin kanssa.

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

expectNoEvents() tarkistaa, ettei mitään lähetetty. cancelAndIgnoreRemainingEvents() peruuttaa keräämisen ja ohittaa odottavat emissi­ot.

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

ViewModelin testaaminen Turbinella

Testatkaa ViewModelin StateFlow injektoimalla valerepository ja tarkistamalla tilasiirtymät.

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

Useiden kerääjien testaaminen turbineScope-funktiolla

turbineScope { } mahdollistaa useiden Flow-virtojen testaamisen samanaikaisesti siten, että kullakin on oma Turbine-instanssinsa.

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

Flow-operaattoreiden testaaminen

Testatkaa mukautettujen Flow-operaattoreiden ja muunnosten toiminta erillään Turbinen avulla.

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

Testaaminen siirtämällä virtuaaliaikaa

Käyttäkää testScheduler.advanceTimeBy()-funktiota virtuaaliajan täsmälliseen hallintaan testeissä, joissa käytetään delay-funktiota.

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

Yleiset virheet

Peruuttakaa Turbine aina kuumien Flow-virtojen testauksen lopuksi. Jos cancelAndIgnoreRemainingEvents() unohtuu, testit jäävät odottamaan uusia tapahtumia.

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

Pikatarkistus

Mikä Turbine-funktio kerää seuraavan lähetetyn alkion ja aiheuttaa poikkeuksen, jos Flow-virta päättyy tai epäonnistuu ensin?

Kertaus

Turbine yksinkertaistaa Flow-virtojen testaamista funktioilla awaitItem(), awaitComplete() ja awaitError(). Käyttäkää virtuaaliaikaan runTest-funktiota. Peruuttakaa kuumien Flow-virtojen testit aina funktiolla cancelAndIgnoreRemainingEvents().

Aloita maksutta

Opi Kotlin tekoälytuutorin avulla — ilmaiseksi

Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.

Kurssit
51
Oppitunnit
203

Usein kysytyt kysymykset

Onko oppitunti ”StateFlow’n ja SharedFlow’n testaaminen Turbinella” ilmainen?

Kyllä — voit lukea täällä verkossa kokonaan ilmaiseksi mitkä tahansa Kotlin Academy-oppimispolun 3 oppituntia, myös oppitunnin “StateFlow’n ja SharedFlow’n testaaminen Turbinella”. Sen jälkeen CoddyKit PRO avaa kaikki oppitunnit sekä interaktiiviset harjoitukset sisäänrakennetulla koodieditorilla ja ympäri vuorokauden toimivalla tekoälytuutorilla. Kotlin Academy-kurssilla on yhteensä 4 oppituntia.

Mitä opin oppitunnilla ”StateFlow’n ja SharedFlow’n testaaminen Turbinella”?

Kirjoittakaa Flow-pohjaisen tilan yksikkötestejä Turbine-testauskirjastolla. Harjoittelet Kotlin Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.

Tarvitsenko kokemusta aloittaakseni Kotlin Academy-opiskelun?

Aiempi kokemus ei ole tarpeen. CoddyKitin Kotlin Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.

Kuinka kauan ”StateFlow’n ja SharedFlow’n testaaminen Turbinella”-oppitunnin suorittaminen kestää?

Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.

Voinko kirjoittaa ja suorittaa koodia tällä Kotlin Academy-oppitunnilla?

Kyllä. Jokainen Kotlin Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.

Kaikki tämän kurssin oppitunnit

  1. StateFlow: käyttöliittymän hot-tilan säilö
  2. SharedFlow: tapahtumaväylät ja kertaluonteiset tapahtumat
  3. Cold Flow -virran muuntaminen hot-virraksi shareIn- ja stateIn-funktioilla
  4. StateFlow’n ja SharedFlow’n testaaminen Turbinella
← Takaisin: Kotlin Academy