Kotest is published as several separate artifacts rather than one jar. Name the main ones and say what each provides — matchers, property testing, data-driven testing, JSON assertions, and the test runner.
answer
- runner-junit5 = executes specs
- assertions-core = the matcher DSL
- property = Arb / Exhaustive / checkAll
- framework-datatest = withData
- extensions live under io.kotest.extensions
basics
~10 skotest-runner-junit5 runs specs; kotest-assertions-core holds the shouldBe/shouldContain matcher family; kotest-property provides Arb/Exhaustive property testing; kotest-framework-datatest provides withData; kotest-assertions-json adds JSON matchers. You add only the modules you use.
solid answer
~50 sKotest is deliberately modular, so a project takes only what it needs: - **kotest-runner-junit5** — the runner that discovers and executes Kotest specs; it is what makes specs run at all. - **kotest-assertions-core** — the core matcher DSL (`shouldBe`, `shouldContain`, collection/string/throwable matchers, `assertSoftly`, clues). - **kotest-property** — property-based testing: `Arb`, `Exhaustive`, `checkAll`/`forAll`. - **kotest-framework-datatest** — data-driven testing (`withData`). - **kotest-assertions-json** — JSON-specific matchers, a separate module so projects that do not test JSON do not pull it in. Beyond these, additional assertion and integration modules live in Kotest's separate *extensions* project under the `io.kotest.extensions` group — Ktor assertions and the Spring, Testcontainers, WireMock and Koin extensions among them. The important consequence is that the assertion modules do not depend on the runner: `kotest-assertions-core` can be used from a non-Kotest test, and conversely you can run Kotest specs and assert with something else.
go deeper
Name the runner, the assertions module, and know that property testing and data-driven testing are separate artifacts.
Add that assertions are runner-independent, that JSON assertions are their own module, and that the integrations live under a separate group.
Explain the adoption consequences — matchers first, specs later — and the version-alignment concern between core artifacts and the independently released extensions.
Treat the module map as a dependency-surface decision: keep the test classpath minimal, align core versions centrally, and adopt an extension only when it manages a real lifecycle.
## Why Kotest is split up Kotest is not a single jar. It is a family of artifacts under the `io.kotest` group, plus a separate ecosystem under `io.kotest.extensions`. The split exists because the pieces are genuinely independent: the assertion DSL, the property-testing engine, the data-driven helpers and the runner are useful on their own, and most projects want only some of them. Modularity also keeps the dependency footprint of a test suite small and lets each piece evolve at its own pace. ## The core artifacts **kotest-runner-junit5** — the execution side. It provides the machinery that discovers spec classes and runs the tests they register. Without it, spec classes exist but nothing executes them. This is the artifact whose name people remember because it is the one that must be present for a Kotest suite to run at all. **kotest-assertions-core** — the matcher library: `shouldBe` / `shouldNotBe`, the collection matchers, string matchers, throwable matchers, `assertSoftly`, and the clue functions. This is by far the most-used module, and it is *runner-independent* — its matchers throw ordinary assertion errors, so they work inside any test that a JVM test framework can run. Teams frequently adopt Kotest assertions long before (or without) adopting Kotest specs. **kotest-property** — the property-based testing engine: the `Arb` (random, shrinking) and `Exhaustive` (enumerated) generator types and the `checkAll` / `forAll` entry points. Also usable from non-Kotest tests. **kotest-framework-datatest** — the data-driven testing support, notably `withData`, which registers one test per data element with a derived name. It is a separate module because it hooks into the framework's test-registration machinery. **kotest-assertions-json** — matchers for JSON documents. Kept separate so projects that never touch JSON do not pull in the extra dependency. Kotest also publishes a **BOM** artifact (`kotest-bom`) so the versions of the core modules can be aligned in one place instead of repeated per dependency. ## The extensions ecosystem Integration modules live in Kotest's separate extensions project, published under the `io.kotest.extensions` group. That is where you find, among others: - **kotest-extensions-spring** — provides `SpringExtension`, which lets specs participate in Spring's test context (the mechanics of Spring test support belong to Spring's own topic, not here); - **kotest-extensions-testcontainers** — attaches Testcontainers containers to the Kotest lifecycle; - **kotest-extensions-wiremock** — attaches a WireMock server to the lifecycle; - **kotest-extensions-koin** — Koin integration; - **kotest-assertions-ktor** — Ktor-specific assertions; - **kotest-assertions-arrow** — matchers for Arrow types. Because these are a separate project, they version independently of the core artifacts; you cannot assume a core version number is also a valid extension version number. Check the coordinates and versions for the specific extension you need rather than copying a core version across. ## What this modularity buys you 1. **Mix and match.** Use Kotest's matchers with another runner, or Kotest specs with another assertion library. Neither side requires the other. 2. **Incremental adoption.** A team can introduce `kotest-assertions-core` into an existing suite as a pure readability improvement, with no change to how tests are discovered or run, and adopt specs later. 3. **Smaller test classpaths.** JSON matchers, property testing, and every integration extension are opt-in. 4. **Clear ownership when something is missing.** "`withData` is unresolved" is a missing datatest module, not a spec-style mistake; "`Arb` is unresolved" is a missing property module. Knowing the map turns a confusing compile error into a one-line fix. ## Common confusions - Assuming one dependency gives you everything — property testing and data-driven testing in particular are separate modules. - Assuming the integration extensions share the core version number; they are a separate project with its own release train. - Thinking the assertion library requires the Kotest runner. It does not. - Reaching for an extension when a few lines of setup would do. Extensions earn their keep when they manage a lifecycle (start/stop a container, reset a mock server between tests); for a one-off, plain code is often simpler. ## Interview framing This is a setup-literacy question. Interviewers ask it to check that a candidate has actually configured a Kotest project rather than inherited one — the tell is whether they know that matchers, property testing and data-driven testing are *separate* modules, and that the integration extensions live in their own group.
- Can you use Kotest's matchers without using Kotest to run your tests?Yes — the assertion modules are independent of the runner. Matchers signal failure with ordinary assertion errors, so they work in any test a JVM test framework can execute. This is the usual incremental-adoption path: bring in the matcher module for readability first, and decide about spec styles separately.
- A colleague's `withData(...)` call does not resolve even though Kotest runs fine. What is your first guess?The data-driven module is missing — `withData` lives in Kotest's separate datatest artifact, not in the core runner or assertions. The same class of mistake produces unresolved `Arb`/`checkAll` when the property module is absent. Because Kotest is deliberately modular, an unresolved API is usually a missing artifact rather than a wrong import path.
saying these in an interview costs you the question
- Believing a single Kotest dependency provides matchers, property testing and data-driven testing together
- Thinking Kotest's matcher library requires Kotest's own runner
- Assuming the integration extensions share the core artifacts' version number
- Inventing artifact names instead of checking the coordinates for the extension you need