Inject Dispatcher

ID

kotlin.inject_dispatcher

Severity

info

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Best Practice

Language

Kotlin

Tags

best-practice, concurrency

Description

Reports direct references to the well-known kotlinx.coroutines.Dispatchers singletons — Dispatchers.IO, Dispatchers.Default, Dispatchers.Main, Dispatchers.Unconfined.

Hard-coding a dispatcher at a call site makes the surrounding code untestable: tests cannot substitute a TestDispatcher or a synchronous deterministic dispatcher without rewriting the producer.

Rationale

// Bad — Dispatchers.IO is baked into the call site
class Loader {
    suspend fun load() = withContext(Dispatchers.IO) {
        readFromDisk()
    }
}

The function above cannot be unit-tested without actually scheduling work on the I/O dispatcher. There is no seam through which a test can inject a deterministic dispatcher and assert that withContext was used correctly.

Remediation

Take a CoroutineDispatcher as a constructor parameter (defaulting to the production dispatcher) and pass that name to withContext and coroutine builders.

class Loader(private val io: CoroutineDispatcher = Dispatchers.IO) {
    suspend fun load() = withContext(io) {
        readFromDisk()
    }
}

In tests the constructor becomes the injection point:

val loader = Loader(io = StandardTestDispatcher())