Testing Coroutines & Flow Flashcards
TESTING › Unit
- What does runTest give you, and which dispatcher does it use by default?
- runTest is a coroutine builder for tests that runs the body inside a TestScope backed by a TestCoroutineScheduler. By default it uses StandardTestDispatcher, auto-skips delays via virtual time, and waits for all scheduled coroutines to finish before returning.
- StandardTestDispatcher vs UnconfinedTestDispatcher: what is the difference and when do you use each?
- StandardTestDispatcher queues newly launched coroutines and runs them only when you advance time (advanceUntilIdle/runCurrent), giving precise ordering control for concurrency tests. UnconfinedTestDispatcher runs new coroutines eagerly on the current thread until their first suspension, which is simpler but does not mirror real scheduling.
- Compare advanceUntilIdle(), advanceTimeBy(n), and runCurrent().
- advanceUntilIdle() runs every queued task until the scheduler is empty. advanceTimeBy(n) moves virtual time forward by n and runs everything scheduled strictly before that point. runCurrent() runs only the tasks already scheduled at the current virtual time.
- Why do local unit tests need Dispatchers.setMain(), and how is it usually wired?
- Dispatchers.Main needs a real Looper that does not exist on the JVM, and APIs like viewModelScope hardcode Main. You call Dispatchers.setMain(testDispatcher) before the test and Dispatchers.resetMain() after, commonly via a JUnit MainDispatcherRule (a TestWatcher) so every test gets it automatically.
- How does runTest handle delay() so tests run instantly?
- Test dispatchers use the TestCoroutineScheduler's virtual clock, so delay() does not actually wait wall-clock time; the scheduler skips/advances over it. The top-level runTest body auto-advances virtual time, so delays in it are effectively instant, while child coroutines on StandardTestDispatcher still need time advanced.
- What is TestScope.backgroundScope for?
- backgroundScope runs coroutines that never complete on their own (e.g. collecting a hot flow or an infinite loop). They are automatically cancelled when the test ends, so runTest will not hang waiting for them like it would for work launched in the main TestScope.
- Why must all TestDispatchers in one test share a single scheduler?
- A single TestCoroutineScheduler is the one virtual clock; sharing it keeps all coroutines on the same timeline so advancing time affects everything. When you call setMain with a test dispatcher, new TestDispatchers created afterwards automatically adopt Main's scheduler; otherwise pass testScheduler explicitly.
- In Turbine, what do awaitItem(), awaitComplete(), and awaitError() do, and what happens to unconsumed events?
- awaitItem() suspends for and returns the next emission, awaitComplete() asserts normal completion, and awaitError() asserts the flow finished with a Throwable. If the test{} block ends with events still unconsumed, Turbine throws a TurbineAssertionError listing them, unless you call cancelAndIgnoreRemainingEvents().
- How do you unit test a ViewModel that exposes state via StateFlow?
- Inject fake repositories and a test dispatcher, install a MainDispatcherRule so viewModelScope uses the test dispatcher, then in runTest trigger the action, advanceUntilIdle() (for StandardTestDispatcher), and assert state.value or collect emissions with Turbine, often collecting into backgroundScope.