testing-flows-with-turbine

Assert Kotlin Flow emissions in tests using Cash App Turbine.

1|Updated Jul 6, 2026
One-click install
npx skills add https://github.com/citytexi/team-yg-pesonal-agent --skill testing-flows-with-turbine-citytexi
Or copy as Structured Prompt for Agent▼
Please help me install this Agent Skill.
Skill: testing-flows-with-turbine
Source: https://github.com/citytexi/team-yg-pesonal-agent/tree/main/.claude/skills/testing-flows-with-turbine
Command: npx skills add https://github.com/citytexi/team-yg-pesonal-agent --skill testing-flows-with-turbine-citytexi

SYSTEM DOCUMENTATION & REQUIREMENTS

💡 This Skill requires app.cash.turbine:turbine.

What problem does it solve? Testing Kotlin Flows with flow.toList() hangs forever on hot flows like StateFlow and SharedFlow because they never complete, and raw collection cannot express assertions like "no more events" or "the next event is an error". This Skill teaches how to use Cash App Turbine to assert Flow emissions one at a time with built-in cancellation and timeout handling. ## Core Features & Use Cases - Flow assertion API: Covers flow.test { }, awaitItem, awaitComplete, awaitError, expectMostRecentItem, skipItems, expectNoEvents, and the non-suspending takeItem/takeComplete/takeError variants. - Hot vs cold flow contract: Explains why StateFlow emits its seed value first (replay = 1), why SharedFlow never completes, and why every hot-flow test must end with cancel() or cancelAndIgnoreRemainingEvents(). - Multi-flow and callback testing: Uses turbineScope/testIn for interleaved assertions across multiple flows, and the standalone Turbine<T> channel to verify non-Flow callbacks and listeners. - Use Case: A ViewModel exposes uiState: StateFlow<UiState> and events: SharedFlow<Event>. Use this Skill to write a runTest that asserts the seed state, triggers login(), and verifies the Loading → Success state sequence plus the LoggedIn event without hanging. ## Quick Start Use the testing-flows-with-turbine skill to write a Turbine test that asserts my ViewModel's StateFlow emissions.

Frequently Asked Questions about testing-flows-with-turbine

High-intent search queries and answers about installing and using this skill.

FAQPage Schema
How do I test a Kotlin StateFlow or SharedFlow without the test hanging?▼

Use Turbine's flow.test { } block instead of flow.toList(). Hot flows never complete, so toList() suspends forever; Turbine collects emissions into a channel and lets you assert each one with awaitItem(), then cancelAndIgnoreRemainingEvents() to finish.

Turbine vs flow.toList() — which should I use for Flow testing?▼

Use flow.toList() for cold flows that complete deterministically and where you only assert the full list. Use Turbine for hot flows (StateFlow/SharedFlow), intermediate-state assertions, error or completion checks, and multi-flow tests.

Why does awaitItem() return the wrong first value on a StateFlow?▼

StateFlow has replay = 1, so the collector immediately receives the current seed value before any new emission. Either assert the seed with the first awaitItem() or call skipItems(1) before triggering new emissions.

Does Turbine work with runTest virtual time and advanceTimeBy?▼

Turbine's default 3-second timeout is wall clock and is not driven by runTest's virtual TestCoroutineScheduler. If an emission genuinely takes longer than 3 real seconds, override it with a per-call timeout parameter or withTurbineTimeout.

How do I test multiple flows at the same time with Turbine?▼

Use turbineScope { } with flow.testIn(backgroundScope) for each flow, which returns independent ReceiveTurbine handles you can assert in interleaved order. Each testIn turbine must be cancelled before the turbineScope block ends.

Can Turbine verify callbacks or listeners that are not Flows?▼

Yes. The standalone Turbine<T>() is a send-and-receive channel where production code pushes values via add() and the test asserts them with awaitItem(). Call ensureAllEventsConsumed() to confirm no extra invocations occurred.