liveBook · Manning

Chapter 18: Error handling and testing

What you will learn in chapter 18 of Kotlin in Action, 2E

The introduction explains why robust concurrent Kotlin applications need graceful error handling and testing when components or external services fail.

After this chapter you will be able to

What it builds on

Section by section

Introduction

The introduction explains why robust concurrent Kotlin applications need graceful error handling and testing when components or external services fail.

Key terms: concurrent applications, error handling, testing

18.1 Handling errors thrown inside coroutines

Explains where to catch exceptions thrown in coroutines and why catching an async result at await does not stop the original exception from reaching the parent.

Key terms: try-catch, launch, async, coroutine boundaries, await, parent coroutine, uncaught exception

18.2 Error propagation in Kotlin coroutines

This section explains when a child coroutine’s failure should bring down its parent and how a regular Job or SupervisorJob determines that behavior.

Key terms: concurrent decomposition of work, supervising execution, parent coroutine, child coroutine, regular Job, SupervisorJob, coroutine context

18.2.1 Coroutines cancel all their children when one child fails

This subsection explains how an uncaught failure in one child coroutine propagates through a regular coroutine hierarchy and cancels its siblings.

Key terms: child coroutine, parent, sibling coroutines, uncaught exception, async, launch, scope, common result, resources

18.2.2 Structured concurrency only affects exceptions thrown across coroutine boundaries

This section explains that coroutine exception propagation and sibling cancellation apply when an uncaught exception crosses a coroutine boundary, and shows why catching it inside the coroutine avoids that behavior.

Key terms: uncaught exceptions, coroutine boundaries, try-catch block, sibling coroutines, cancellation exceptions, supertypes, rethrow

18.2.3 Supervisors prevent parents and siblings from being cancelled

This section explains how supervisors isolate child-coroutine failures and why they are useful for long-lived coroutine scopes.

Key terms: supervisor, SupervisorJob, Job, structured concurrency, supervisorScope, launch, heartbeat coroutine, CoroutineExceptionHandler, Application scope, PipelineContext

18.3 CoroutineExceptionHandler: The last resort for processing exceptions

The section explains where uncaught coroutine exceptions are handled and how a custom CoroutineExceptionHandler changes the default behavior.

Key terms: child coroutine, root coroutine, supervisor, CoroutineExceptionHandler, coroutine context, system-wide exception handler, SupervisorJob

18.3.1 Differences when using CoroutineExceptionHandler with launch or async

This section explains why a coroutine exception handler is invoked for a topmost launch coroutine but not a topmost async coroutine, and clarifies that this difference does not prevent cancellation.

Key terms: CoroutineExceptionHandler, launch, async, Deferred, await(), SupervisorJob, cancellation, scope

18.4 Handling errors in flows

This section shows that errors in a flow surface when it is collected and can be handled around the call to collect.

Key terms: flow, collect, try-catch, intermediate operators, CancellationException

18.4.1 Processing upstream exceptions with the catch operator

This section explains how a flow’s catch operator handles upstream exceptions and where it must be placed to handle errors in a pipeline.

Key terms: catch, cancellation exceptions, emit, downstream flow, upstream, downstream, onEach, collect, try-catch

18.4.2 Retry the collection of a flow if predicate is true: The retry operator

Explains how the flow retry operator handles upstream exceptions by recollecting the flow, and why repeated upstream work matters.

Key terms: retry operator, upstream exceptions, lambda, maximum number of retries, intermediate operations, side effects, upstream operators, idempotent

18.5 Testing coroutines and flows

This section introduces runTest for coroutine tests and explains why real-time delays make runBlocking-based tests slow, motivating virtual time.

Key terms: runTest, runBlocking, virtual time

18.5.1 Making tests using coroutines fast: Virtual time and the test dispatcher

This section explains how runTest uses virtual time to speed up coroutine tests and how to control scheduled work while making assertions.

Key terms: runTest, virtual time, test dispatcher, scheduler, single-threaded dispatcher, suspension points, yield, TestScope, TestCoroutineScheduler, delay

18.5.2 Testing flows with Turbine

This section shows how to test finite flows by collecting their values and how Turbine supports tests of more complex flows.

Key terms: runTest, toList, finite flow, Turbine, test, awaitItem, awaitComplete, awaitError

18.6 Summary

Concepts explained in this chapter

← Chapter 17: Flow operators

These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.