ทดสอบ StateFlow และ SharedFlow ด้วย Turbine
เขียนการทดสอบหน่วยสำหรับสถานะที่อาศัย Flow ด้วยไลบรารีทดสอบ Turbine
ทดสอบ StateFlow และ SharedFlow ด้วย Turbine เป็นบทเรียน Kotlin Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Kotlin Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน
ทำไมต้องใช้ Turbine
Turbine เป็นไลบรารีสำหรับทดสอบ Kotlin Flow ที่ช่วยให้ตรวจสอบการปล่อยค่าได้ง่าย หากไม่มีไลบรารีนี้ การทดสอบโฟลว์จะต้องรวบรวมข้อมูลจากช่องด้วยตนเองพร้อมกำหนดเวลาหมด
// 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") }การทดสอบโฟลว์เบื้องต้นด้วย test()
flow.test { } รวบรวมโฟลว์และช่วยให้คุณตรวจสอบการปล่อยค่าแต่ละค่าได้ด้วย awaitItem(), awaitComplete() และ 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
StateFlow จะปล่อยค่าปัจจุบันของตนเองเสมอเมื่อเริ่มรวบรวมข้อมูล awaitItem() ของ Turbine จะรับค่าที่ปล่อยออกมาในครั้งแรกนั้น
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
SharedFlow ที่มี replay=0 จะปล่อยเฉพาะเหตุการณ์ในอนาคต ให้เริ่มรวบรวมข้อมูลก่อน แล้วจึงปล่อยค่า
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 กับเวลาเสมือน
runTest ใช้เวลาเสมือน การเรียก delay() จะเลื่อนเวลาเสมือนไปข้างหน้าในทันที ทำให้การทดสอบที่มีการหน่วงเวลาทำงานได้รวดเร็ว
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 สำหรับการทดสอบข้อยกเว้น
awaitError() ตรวจสอบว่าโฟลว์จบลงพร้อมข้อยกเว้น ให้ใช้ร่วมกับชนิดข้อยกเว้นที่คาดไว้
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 และ cancelAndIgnoreRemainingEvents
expectNoEvents() ตรวจสอบว่าไม่มีการปล่อยค่าใด ๆ cancelAndIgnoreRemainingEvents() ยกเลิกการรวบรวมข้อมูลโดยไม่สนใจการปล่อยค่าที่ค้างอยู่
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()
}
}การทดสอบ ViewModel ด้วย Turbine
ทดสอบ StateFlow ของ ViewModel ด้วยการใส่คลังข้อมูลจำลองเข้าไป แล้วตรวจสอบการเปลี่ยนแปลงของสถานะ
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()
}
}ตัวเก็บข้อมูลหลายตัวด้วย turbineScope
turbineScope { } ช่วยให้คุณทดสอบหลายโฟลว์พร้อมกัน โดยแต่ละโฟลว์มีอินสแตนซ์ 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()
}
}การทดสอบตัวดำเนินการของโฟลว์
ใช้ 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()
}
}การทดสอบด้วยการเลื่อนเวลาเทียม
ใช้ testScheduler.advanceTimeBy() เพื่อควบคุมเวลาเสมือนอย่างแม่นยำในการทดสอบที่เกี่ยวข้องกับ 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()
}
}ข้อผิดพลาดที่พบบ่อย
ยกเลิก Turbine เสมอเมื่อสิ้นสุดการทดสอบโฟลว์ร้อน หากลืมใช้ cancelAndIgnoreRemainingEvents() การทดสอบจะค้างระหว่างรอเหตุการณ์เพิ่มเติม
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()
}
}ตรวจสอบอย่างรวดเร็ว
ฟังก์ชัน Turbine ใดที่รวบรวมรายการที่ถูกปล่อยออกมาถัดไป และแสดงข้อผิดพลาดหากโฟลว์เสร็จสิ้นหรือเกิดข้อผิดพลาดก่อน
ทบทวน
Turbine ช่วยให้การทดสอบ Flow ง่ายขึ้นด้วย awaitItem(), awaitComplete() และ awaitError() ใช้ runTest สำหรับเวลาเสมือน และยกเลิกการทดสอบโฟลว์ร้อนด้วย cancelAndIgnoreRemainingEvents() เสมอ
เรียนรู้ Kotlin ด้วย AI tutor — ฟรี
เขียนและเรียกใช้โค้ดจริงในเบราว์เซอร์ของคุณ รับความช่วยเหลือทันทีจาก AI tutor 24/7 และเรียนรู้ต่อจากที่คุณหยุดบนเว็บหรือในแอป
- คอร์ส
- 51
- บทเรียน
- 203
คำถามที่พบบ่อย
บทเรียน “ทดสอบ StateFlow และ SharedFlow ด้วย Turbine” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “ทดสอบ StateFlow และ SharedFlow ด้วย Turbine” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Kotlin Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Kotlin Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “ทดสอบ StateFlow และ SharedFlow ด้วย Turbine”
เขียนการทดสอบหน่วยสำหรับสถานะที่อาศัย Flow ด้วยไลบรารีทดสอบ Turbine คุณปฏิบัติ Kotlin Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Kotlin Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน Kotlin Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 4 จากทั้งหมด 4 บทเรียน
บทเรียน “ทดสอบ StateFlow และ SharedFlow ด้วย Turbine” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน Kotlin Academy นี้ได้ไหม
ได้ บทเรียน Kotlin Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ
บทเรียนทั้งหมดในหลักสูตรนี้
- StateFlow: ตัวเก็บสถานะแบบร้อนสำหรับ UI
- SharedFlow: บัสเหตุการณ์และเหตุการณ์แบบครั้งเดียว
- แปลง Cold Flow เป็น Hot ด้วย shareIn และ stateIn
- ทดสอบ StateFlow และ SharedFlow ด้วย Turbine