skip to content

Kotest

Kotest offers several spec layouts, an infix matcher DSL, property-based testing, and data-driven tables. Interviewers bring it up to discuss test readability and whether generated inputs beat hand-picked ones.

part ofKotlinoverview, primer and where to startread it →
on this pageshow

explore

questions

20

What is Kotest's withData and why would you use it instead of writing a separate test for each input?

level: juniorimportance: must knowfreq 55%

answer

  1. io.kotest.datatest.withData
  2. One body, many rows, separate tests
  3. Name from toString() by default
  4. Lives inside context/container
  5. Beats forEach: per-case reporting

basics

~10 s

withData 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 s

withData 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 lines
kotlin
class IsEvenTest : FunSpec({
    context("isEven") {
        withData(2, 4, 6) { n ->
            (n % 2 == 0) shouldBe true
        }
    }
})

go deeper

for a junior

Knows withData runs one body over many inputs and creates a test per input.

for a middle

Explains per-case reporting, default naming via toString(), and placement inside container scopes.

for a senior

Contrasts with forEach loops and JUnit parameterized tests; controls naming and chooses appropriate overloads.

for a principal

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

context

open as a page

What is Kotest's matcher DSL, and how do you assert that a value equals an expected value using shouldBe and shouldNotBe?

level: juniorimportance: must knowfreq 70%

basics

~20 s

Kotest lets you write checks like normal English. You write 'result shouldBe 5' to say result must equal 5, and 'result shouldNotBe 0' to say it must not equal 0. If the check fails, the test fails with a clear message.

open as a page

What is property-based testing in Kotest, and how does forAll differ from a traditional example-based test?

level: juniorimportance: must knowfreq 55%

basics

~10 s

Instead of hand-picking a few inputs, Kotest generates many random inputs and checks that a rule (a property) holds for all of them. forAll runs your check against hundreds of generated values automatically.

open as a page

In Kotest, how do you choose which test-layout style your test file uses, and name the four common spec styles?

level: juniorimportance: must knowfreq 70%

basics

~10 s

You pick a style by which class your test class extends. Common ones are StringSpec, FunSpec, DescribeSpec, and BehaviorSpec. Each just changes how you write and group your tests.

open as a page

Explain how Kotest's table(), row(), and forAll work together for table-driven testing. Show a multi-column example.

level: middleimportance: must knowfreq 50%

basics

~10 s

You build a table from rows, where each row holds several values (the inputs and expected output). Then forAll runs your check once per row, passing that row's values into your lambda.

open as a page

How do you assert that a block of code throws a specific exception in Kotest, and what is the difference between shouldThrow<T> and shouldThrowExactly<T>?

level: middleimportance: must knowfreq 65%

basics

~10 s

Wrap the risky code in shouldThrow<SomeException> { ... }. The test passes only if that code throws that exception type. It also hands you the caught exception so you can check its message.

open as a page

Explain the difference between Arb and Exhaustive generators in Kotest. When would you choose each, and how do you build custom or composed generators?

level: middleimportance: must knowfreq 50%

basics

~10 s

Arb makes random values (plus some edge cases) and suits large input spaces. Exhaustive lists every value and suits small, finite spaces. You build custom ones by mapping, filtering, or combining existing generators.

open as a page

When using withData with a data class per case, how do you produce readable test names, and what are the naming overloads?

level: middleimportance: should knowfreq 40%

basics

~20 s

By default each generated test is named from the value's toString(). With a data class you get the auto-generated toString, which is usually readable. If you want nicer names, give withData a Map (keys become names) or a function that builds the name from each value.

open as a page

Which Kotest matchers would you use to assert membership, size, and ordering on collections and substrings on strings? Give concrete examples.

level: middleimportance: should knowfreq 55%

basics

~10 s

For lists use matchers like shouldContain (has an element), shouldHaveSize (length), shouldBeEmpty, and shouldContainExactly (same elements in order). For strings use shouldContain, shouldStartWith, shouldEndWith, and shouldMatch for a regex.

open as a page

How do you configure iteration count, edge-case injection, seeds, and discards in Kotest property tests, and why do edge cases matter?

level: middleimportance: should knowfreq 35%

basics

~20 s

You can set how many random inputs to try, control the share of special edge values, fix a seed so failures repeat, and limit how many inputs may be skipped. Edge cases catch boundary bugs like zero or empty.

open as a page

Write a BehaviorSpec test and explain how given/when/then map to nesting, including how Kotest avoids clashing with Kotlin's `when` keyword.

level: middleimportance: should knowfreq 55%

basics

~10 s

BehaviorSpec groups tests as given (a context), when (an action), then (the assertion). Because when is a Kotlin keyword, you either backtick it or use the capitalized When alias.

open as a page

Compare FunSpec and DescribeSpec for nesting tests. How do `test`/`context` and `describe`/`it` work, and when would you choose one over the other?

level: middleimportance: should knowfreq 50%

basics

~10 s

FunSpec uses test("...") for cases and context("...") to group them. DescribeSpec uses describe("...") to group and it("...") for cases, like Jest/RSpec. Both nest; pick by team style.

open as a page

Can withData be nested, and can its lambda call suspend functions? Explain how nesting and coroutine support work.

level: seniorimportance: should knowfreq 32%

basics

~20 s

Yes 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.

open as a page

What problem does assertSoftly solve in Kotest, and how does it change the behavior of multiple matcher assertions inside its block?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Normally a test stops at the first failed check, hiding the rest. assertSoftly runs all the checks in its block, collects every failure, and reports them together at the end, so you see all problems in one run.

open as a page

What is shrinking in Kotest property tests, why does it matter, and how does it interact with custom Arb generators?

level: seniorimportance: should knowfreq 40%

basics

~20 s

When a property fails, Kotest tries simpler versions of the failing input until it finds the smallest one that still breaks the test. That minimal counterexample makes debugging far easier than a huge random value.

open as a page

How does Kotest construct spec instances and run the registration lambda, and what is the default isolation mode across spec styles?

level: seniorimportance: should knowfreq 35%

basics

~20 s

Kotest builds your spec, runs the lambda/init block once to register all tests, then runs them. By default one spec instance is reused for every test in that class, so shared state leaks unless you change isolation mode.

open as a page

As a tech lead, how do you decide between withData/table-driven tests and property-based forAll, and what are the trade-offs of each for a given test surface?

level: principalimportance: should knowfreq 24%

basics

~20 s

Use table-driven tests (withData/table) when you have specific, known examples and exact expected outputs. Use property-based forAll when you want many random inputs to check a general rule. Tables document edge cases; properties find unknown ones.

open as a page

How do you write a reusable custom matcher in Kotest, and how do you compose matchers with and/or and negation?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

You create a small object implementing Kotest's Matcher interface, returning whether the value passed plus messages for failure and for the negated case. Then you can combine matchers with .and() / .or() and flip them with .invert().

open as a page

When is property-based testing the right tool versus example tests, and how do you design properties (laws, round-trips, oracles) that avoid tautologies and flakiness?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Use property tests when there's a general rule the code must always obey, like 'encode then decode returns the original'. Pick rules that don't just re-run the code under test, and keep generators deterministic so tests don't flake.

open as a page

Your team is standardizing Kotest spec styles across a large codebase. What trade-offs and pitfalls (test naming, focus/bang prefixes, nesting depth) guide picking and enforcing a style?

level: principalimportance: nice to knowfreq 18%

basics

~20 s

Pick one or two styles, match them to test types (unit vs BDD), and standardize naming. Watch for deep nesting hurting readability and for prefix features like focusing or disabling tests behaving consistently across styles.

open as a page