Can withData be nested, and can its lambda call suspend functions? Explain how nesting and coroutine support work.
answer
- Lambda is suspend -> call suspend funs directly
- Nest withData for cartesian matrix
- Each row is a real test (beforeTest/afterTest per row)
- Watch test-count explosion
- No runBlocking needed
basics
~20 sYes to both. You can put a withData inside another withData (or inside a context) to build a grid of cases, and the test lambda is a suspend block, so you can directly call suspending functions without extra wrappers.
solid answer
~40 swithData's block is a suspending lambda running in Kotest's coroutine-based test scope, so you can call suspend functions (e.g. a suspending service, runBlocking is unnecessary) directly inside it. withData can also be nested: calling withData again inside a withData block (or inside a context container) produces a cartesian-style tree of generated tests, each leaf an independent test. This is handy for matrix testing (e.g., every browser x every locale). Because each row is its own test, Kotest lifecycle callbacks (beforeTest/afterTest) and per-test config apply per row. You can attach config via .config or the config-aware overloads (timeouts, tags) where supported. Keep nesting shallow for readability; deep cartesian products explode test counts. The suspend support is why withData pairs cleanly with coroutine code and Kotest's runTest-style flows.
code
kotlin · 7 linescontext("grid") {
withData(1, 2) { x ->
withData(10, 20) { y ->
(x + y) shouldBe x + y // 4 leaf tests: (1,10)(1,20)(2,10)(2,20)
}
}
}go deeper
Knows the lambda can call suspend functions and that nesting is possible.
Builds a nested matrix and understands each leaf is its own test.
Explains suspend-scope mechanics, per-row lifecycle/config, and manages combinatorial explosion deliberately.
Weighs matrix testing cost vs. value, sets guardrails on nesting depth, and integrates coroutine test strategy (runTest/virtual time) across the suite.
## Coroutine (suspend) support Kotest test bodies run inside a **coroutine scope**, and `withData`'s lambda is a **`suspend` lambda**. That means you can **call suspend functions directly** — no `runBlocking` wrapper: ```kotlin import io.kotest.core.spec.style.FunSpec import io.kotest.datatest.withData import io.kotest.matchers.shouldBe class FetchTest : FunSpec({ context("fetch user") { withData(1L, 2L, 3L) { id -> val user = userService.fetch(id) // suspend fun user.id shouldBe id } } }) ``` The coroutine context comes from Kotest's framework; for testing flows/virtual time you typically combine your own `runTest` inside the body or use Kotest extensions. ## Nesting withData You can **nest** `withData` to build a **matrix** of cases. Each inner row becomes a leaf test under its outer parent: ```kotlin context("matrix") { withData("chrome", "firefox") { browser -> withData("en", "de") { locale -> render(browser, locale) shouldNotBe null } } } ``` This yields a cartesian product: chrome/en, chrome/de, firefox/en, firefox/de — **four leaf tests**, each independently reported. ## Lifecycle and config per row Because **each generated row is a real Kotest test**: - `beforeTest`/`afterTest` callbacks fire **per row**. - You can attach per-test config (timeouts, tags, retries) via the `.config { ... }` block or config-aware overloads where the spec style supports it. - Failures are isolated to their leaf. ## Cautions - **Test-count explosion:** nested cartesian products multiply; `3 x 4 x 5` = 60 tests. Keep nesting shallow and intentional. - **Readability:** deeply nested `withData` can obscure intent; sometimes a single data class with all fields plus one `withData` is clearer. - **Shared mutable state** across rows is risky because rows are independent tests; prefer per-row setup. ## Key takeaway `withData` is **coroutine-friendly** (suspend lambda) and **nestable** (matrix grids), with each row a first-class test honoring lifecycle and config — but watch the combinatorial blow-up.
- Do you need runBlocking inside a withData lambda to call a suspend function?No. The withData lambda is a suspend block in Kotest's coroutine scope, so you can invoke suspend functions directly.
- What is the main downside of deeply nested withData?Combinatorial test-count explosion and reduced readability; a cartesian product multiplies quickly, so keep nesting shallow or use a single case data class.
Nested withData is like nested for-loops that each emit a labeled test instead of just running code.
saying these in an interview costs you the question
- Wrapping the body in runBlocking unnecessarily
- Believing withData cannot be nested
- Thinking lifecycle callbacks fire once for the whole block, not per row
- Ignoring combinatorial explosion in nested matrices
- Sharing mutable state across rows assuming sequential single-test semantics