skip to content

Kotest lets you disable a test by using an x-prefixed builder (xtest, xcontext, xdescribe, xgiven), or by prefixing the test name with a bang (!), and lets you prefix a name with f: to focus it. Explain what each does, which tests they can be applied to, and which one wins when several are in play.

level: juniorimportance: should knowfreq 40%

answer

  1. x-builder = registered but skipped
  2. ! bang = disable by name, works in StringSpec
  3. f: focus = only focused root tests, per spec
  4. bang beats focus
  5. root-level only; disabled container body never runs

basics

~20 s

x-builders and the ! bang prefix both register a test as disabled, so it is reported as skipped and a disabled container skips its whole subtree. f: focus runs only focused root tests in that spec. Bang beats focus, and both prefixes only work on root-level tests.

solid answer

~50 s

Kotest gives three name-level switches. **x-prefixed builders** exist in every style (xtest/xcontext, xdescribe/xit, xgiven/xwhen/xthen, xshould). They register the test but mark it disabled, so it is reported as skipped rather than disappearing the way a commented-out test does. The container body never runs, so everything nested under it is skipped as one unit. **Bang (!)** on the test name does the same thing without needing a special builder, which is how you disable a StringSpec test whose name is just a string. **f: focus** is the inverse: mark one or more root tests with f: and Kotest runs only those in that spec. Focus and bang are supported on **root-level tests only**, not nested ones, and focus is scoped to the spec it appears in, not the whole run. Bang takes precedence, so an f: test that is also disabled stays disabled. Focus is a local dev-loop tool; committing it silently shrinks CI, so ban it in review.

code

kotlin · 14 lines
kotlin
class OrderSpec : FunSpec({

   test("normal test") { }

   xtest("parked until PAY-412 lands") { }

   xcontext("whole subtree skipped") {
      test("never even registered") { }
   }

   test("!disabled by bang prefix") { }

   test("f:only this one runs while focused") { }
})

go deeper

for a junior

Know that x-builders and ! disable a test while keeping it visible as skipped, and that f: is a temporary local focus you never commit.

for a middle

Add the mechanics: a disabled container's lambda never runs, so the subtree is skipped wholesale; bang beats focus; both prefixes are root-level only.

for a senior

Frame it as debt policy — parked tests need a ticket reference, and CI should grep for committed f: prefixes since they shrink the suite while staying green.

for a principal

Argue when a test should be deleted rather than parked, and how the disable mechanism interacts with the team's tag strategy for environment-dependent suites.

## Where these switches live Kotest spec styles register tests by calling builder functions inside the spec body: `test("...")`, `context("...")`, `describe("...")`, `given("...")`. Two of the three switches here are part of that registration call itself — either the builder you pick or the string you pass — which is why they are cheap to apply and cheap to grep for. ## x-prefixed builders Every style ships an `x` twin of its builders: `xtest`/`xcontext` (FunSpec), `xdescribe`/`xit`/`xcontext` (DescribeSpec), `xgiven`/`xwhen`/`xthen`/`xand` (BehaviorSpec), `xshould` (ShouldSpec). The test case is still registered, with its name, but flagged disabled, so the engine emits a skipped/ignored result for it. That visibility is the whole point over commenting the code out: a commented test vanishes from the report and rots against refactors because it is no longer compiled; an `xtest` still compiles and still shows up as debt in every run. Disabling a container disables the subtree. The container's lambda is not executed, so its children are never registered at all — you will not see individual skipped entries for them, just the skipped container. ## The bang prefix Prefixing a test name with `!` disables it: `"!slow login test" { ... }`. This is the only option in StringSpec, where the test *is* the string and there is no builder to swap. It works in the other styles too and is the quickest one-character disable. ## Focus Prefixing a root test name with `f:` focuses it. When at least one root test in a spec is focused, Kotest runs only the focused root tests in that spec and skips the rest. Two limits matter: focus applies to **root-level tests only** (nesting an `f:` inside a container does nothing useful), and it is **per spec** — other specs in the run are unaffected, so `f:` is not a way to run one test across the whole suite. If a test is both focused and banged, the bang wins and it stays disabled. ## Choosing between them, and the alternative Use `x` builders or `!` for a test you are deliberately parking, with a comment or ticket saying why. Use `f:` only in your editor loop and never commit it — a committed `f:` quietly reduces the spec CI runs to one test while the build stays green, which is far worse than a red test. A grep in CI for `f:` in test sources is a cheap guard. When the disable is conditional, or you want a recorded reason, none of these prefixes fit — they are static. Kotest's test config parameters (`enabled`, `enabledIf`, `enabledOrReasonIf`) cover that case, and spec-level annotations cover disabling a whole class. The prefixes are for the flat, unconditional, checked-in-for-now case.

  • Why prefer an x-prefixed builder over commenting the test out?
    A commented-out test disappears from the report, so nobody is reminded it exists, and it stops being compiled — it silently drifts out of sync with the production code it was testing. An x-prefixed test still compiles against the current API and is reported as skipped on every run, which keeps it visible as debt and makes re-enabling it a one-character change.
  • A developer commits a test named "f:checkout succeeds". What is the practical damage?
    Only that focused root test runs in that spec; every other root test in the same spec is skipped, and the build still reports green. Coverage silently drops with no failing signal. It is a good candidate for a CI grep or a lint rule over test sources.

saying these in an interview costs you the question

  • Claiming an x-prefixed test is removed from the report rather than reported as skipped
  • Thinking f: focus applies across the whole suite instead of only the spec it appears in
  • Expecting focus or bang prefixes to work on nested tests
  • Assuming a disabled container still registers and reports its nested children individually

context