What is Kotest's withData and why would you use it instead of writing a separate test for each input?
answer
- io.kotest.datatest.withData
- One body, many rows, separate tests
- Name from toString() by default
- Lives inside context/container
- Beats forEach: per-case reporting
basics
~10 swithData runs the same test body once for each value you give it. Instead of copy-pasting a test many times, you list the inputs and Kotest creates a separate test for each one automatically.
solid answer
~40 swithData is Kotest's data-driven testing helper (from the io.kotest.datatest package). You pass it a collection of inputs and a lambda; Kotest invokes that lambda once per input, generating a distinct, individually-reported test for each row. This avoids duplicating assertions and gives you per-case pass/fail results so you immediately see which input broke. It works inside container scopes like context (in FunSpec/BehaviorSpec) or as a top-level test in spec styles that support it. Test names are derived automatically from each element's toString(), or you can supply explicit names via a Map or a nameFn. Compared to a loop with forEach inside one test, withData reports each case separately and keeps running the rest after one fails.
code
kotlin · 7 linesclass IsEvenTest : FunSpec({
context("isEven") {
withData(2, 4, 6) { n ->
(n % 2 == 0) shouldBe true
}
}
})go deeper
Knows withData runs one body over many inputs and creates a test per input.
Explains per-case reporting, default naming via toString(), and placement inside container scopes.
Contrasts with forEach loops and JUnit parameterized tests; controls naming and chooses appropriate overloads.
Frames data-driven tests within a team testing strategy: when table tests aid readability vs. when property-based tests are stronger.
## What withData is `withData` is Kotest's primary **data-driven (table-driven) testing** tool. It lives in the `io.kotest.datatest` package and lets you run **one test body across many input rows**, with Kotest generating a **separate, independently-reported test** for each row. ## Why not just a loop If you write `listOf(1,2,3).forEach { assert(...) }` inside a single test, all cases share one test result. The first failure aborts the rest, and the report shows one test, not which input failed. `withData` instead creates N tests, so: - Each case has its **own name** in the report. - A failure in case 2 does not stop cases 3..N. - You see exactly **which input** failed. ## Basic usage ```kotlin import io.kotest.core.spec.style.FunSpec import io.kotest.datatest.withData import io.kotest.matchers.shouldBe class SquareTest : FunSpec({ context("square of n") { withData(1, 2, 3) { n -> (n * n) shouldBe n * n } } }) ``` Here `withData(1, 2, 3)` runs the lambda three times. Kotest derives each test name from the element's `toString()` (e.g. "1", "2", "3"). ## Where it can be called - Inside a **container scope** such as `context("...") { ... }` in `FunSpec`, or `Given/When` in `BehaviorSpec`. - As a top-level data test in styles that allow it. - It is **not** a matcher; it is a test-generating construct, so it belongs where tests/containers are declared, not inside an assertion. ## Naming - Default: each element's `toString()` becomes the test name. - Custom: pass a `Map<String, T>` (keys are names) or use the `nameFn` overload `withData(nameFn = { "case $it" }, data) { ... }`. ## Key takeaway Reach for `withData` whenever you have **the same assertions over many inputs** and want clear per-case reporting.
- How are the test names generated by default?From each data element's toString(). For readable names with custom types, override toString(), pass a Map of name->value, or use the nameFn overload.
- What import do you need?import io.kotest.datatest.withData (the data-driven helpers are in the kotest-framework-datatest module / io.kotest.datatest package).
Like a stamp: you define the shape once and press it onto each piece of paper (input) to get an individual result.
saying these in an interview costs you the question
- Claiming withData is a matcher or assertion
- Saying it is identical to a forEach loop (loses per-case reporting and continues-on-failure)
- Thinking it stops after the first failing row
- Not knowing the io.kotest.datatest package
- Believing it requires JUnit @ParameterizedTest