skip to content

What does the Kotest IntelliJ plugin add over running Kotest specs through the IDE's ordinary test view, and what do you fall back on without it?

level: middleimportance: nice to knowfreq 22%

answer

  1. tests are runtime lambdas with computed names → not addressable by method
  2. plugin = gutter icons per DSL block + run one nested test
  3. navigation from result back to the DSL line
  4. fallback: run the whole spec class
  5. f: focus (top-level only), ! disable, tags for durable selection

basics

~20 s

It adds gutter run icons for each spec and each individual test or container block, so you can run one nested test directly — something the ordinary class/method-based view cannot address, because nested test names are runtime strings inside lambdas. Without it you run whole specs and fall back on Kotest's own focus, bang and tag mechanisms.

solid answer

~60 s

The plugin's main value is **addressability**. A Kotest spec is a class whose tests are lambdas registered at run time, with names that may be computed in a loop. A test view built around classes and methods can therefore offer you the spec class and nothing finer. The plugin understands the DSL: it puts run icons in the gutter next to each `test` / `context` / `describe` block, and running one sends the engine a filter identifying that spec and test path, so a single nested test executes on its own. It also improves the round trip — navigating from a result in the tree back to the DSL line, plus editor conveniences such as templates and inspections for spec code. Without it you can still run everything: specs are ordinary classes, so a spec-level run works. To narrow further you use Kotest's own mechanisms — prefixing a top-level test name with `f:` to focus it, `!` or `.config(enabled = false)` to disable others, or tag filtering via `kotest.tags`. Those work everywhere, including CI, but they mean editing source to change what runs.

code

kotlin · 11 lines
kotlin
class OrderSpec : FunSpec({
    test("f:only this top-level test runs") { }  // focus - never commit this
    test("!parked for now") { }                  // bang - disabled
    test("skipped while focus is active") { }

    context("nested names computed at run time") {
        listOf("EU", "US").forEach { region ->
            test("is rejected in $region") { }    // no method with this name exists
        }
    }
})

go deeper

for a junior

Know the plugin gives gutter icons and single-test runs, and that specs can still be run as whole classes without it.

for a middle

Explain why runtime-registered lambdas with computed names cannot be addressed by a class/method view, and list the source-level fallbacks.

for a senior

Draw the line between inner-loop tooling and durable, committable selection, and flag committed focus/bang prefixes as a silent coverage risk.

for a principal

Set the team convention: plugin runs for iteration, tags for anything CI or a teammate must reproduce, and a guard against focus prefixes reaching the default branch.

## Why individual Kotest tests are hard to address In a framework where each test is an annotated method, an IDE can name any test with a class plus a method identifier — both exist statically in the bytecode. Kotest is different by design: a spec is a class, and its tests are **lambdas registered when the spec body executes**, keyed by strings. Those strings can be built at run time: ```kotlin listOf("EU", "US").forEach { region -> test("is rejected in $region") { } } ``` There is no method called `is rejected in EU` anywhere. So a test view that thinks in classes and methods can offer you the spec class and stop there. ## What the plugin contributes - **Gutter icons at DSL granularity.** It parses the spec source and puts run/debug icons beside the spec class *and* beside each `test`, `context`, `describe`, `should`, `given` block, including nested ones. - **Running a single nested test.** Choosing one of those icons launches a run that tells the Kotest engine which spec and which test path to execute, so only that test (and the containers on its path) runs. This is the capability the ordinary view cannot provide. - **Navigation from results back to source.** Clicking a result in the tree jumps to the DSL line that produced it, which matters when names are computed and grepping for the literal fails. - **Editor support for spec code** — templates for creating specs and inspections aimed at Kotest usage — small conveniences that reduce the friction of the DSL for people used to annotated methods. ## Life without the plugin Nothing is broken; the granularity is coarser and the way you narrow a run changes from clicking to editing. - **Run the whole spec.** A spec is a concrete class, so a class-level run works from the IDE or from a build invocation. For most specs this is fast enough that finer selection is a convenience, not a necessity. - **Focus a test with `f:`.** Prefixing a **top-level** test name with `f:` marks it focused; other top-level tests in that spec are skipped. It is the quickest local narrowing and needs no tooling — but it lives in source, so it must never be committed. Note the restriction: focus applies to top-level tests, not to arbitrarily nested ones. - **Disable the rest.** A `!` name prefix, an `x`-prefixed builder (`xtest`, `xcontext`), or `.config(enabled = false)` turn individual blocks off. `@Ignored` on the class silences a whole spec. - **Tag filtering.** Kotest `Tag` objects plus the `kotest.tags` system property give a selection mechanism that works identically locally and in CI. This is the mechanism to reach for when the selection is *durable* — a suite of slow tests, a category of integration specs — rather than a five-minute debugging need. ## The judgement call The plugin optimises the inner development loop; the source-level mechanisms are what CI and teammates rely on. So: - Use plugin-driven single-test runs while iterating; they leave no trace in the repository. - Use tags for anything a build or another engineer must reproduce — a run configuration in one person's IDE is not a shared artefact. - Treat `f:` and `!` as strictly temporary. A committed `f:` silently reduces a spec to one test, and the build stays green while coverage quietly disappears. A review rule or a simple search in CI for those prefixes is cheap insurance. ## What an interviewer is checking That you understand *why* the tooling exists: not "the plugin is nicer", but "Kotest tests are runtime-registered values with computed names, so addressing one requires DSL awareness that a class/method view does not have". The fallback list (focus, bang, disable, tags) shows you can work without the plugin and know which mechanisms are safe to commit.

  • Why can an IDE's ordinary class-and-method test view not offer to run a single nested Kotest test?
    Because there is no method to point at. Kotest tests are lambdas registered when the spec body executes, identified by strings that can be computed at run time, so nothing in the compiled class corresponds to an individual nested test. Selecting one requires understanding the DSL source and passing the engine a spec-plus-test-path filter, which is exactly what the Kotest plugin adds.
  • Your colleague commits a test whose name starts with f:. What is the consequence?
    Focus mode activates for that spec, so the other top-level tests in it are skipped while the build still reports green. Coverage silently drops with no failing signal, which is the same silent under-testing risk as a misconfigured filter. Treat committed f: and ! prefixes as review blockers, or grep for them in CI.

saying these in an interview costs you the question

  • Believing the plugin is required for Kotest specs to run at all
  • Assuming an ordinary method-based test view can address individual nested tests
  • Using the f: focus prefix as a durable selection mechanism and committing it
  • Expecting f: to work on arbitrarily nested tests rather than top-level ones
  • Thinking IDE run configurations are a substitute for tags when CI must reproduce a selection

context