skip to content

Spec Styles

The style you get — FunSpec, StringSpec, DescribeSpec, BehaviorSpec — depends on which base class the test extends. Picking one and applying it consistently across a codebase matters more than which one you pick.

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

questions

5

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%

answer

  1. No @Test — extend a base class
  2. StringSpec, FunSpec, DescribeSpec, BehaviorSpec
  3. Style = base class, not annotation
  4. init { } or constructor lambda
  5. Same matchers/lifecycle, different layout

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.

solid answer

~30 s

Kotest has no @Test annotation. Instead, your test class extends a base spec class, and that base class dictates the DSL you write tests in. The four common styles are: `StringSpec` (a string literal followed by a lambda), `FunSpec` (calls to `test("...") { }`, groupable with `context("...")`), `DescribeSpec` (`describe`/`it`, RSpec-like, nestable), and `BehaviorSpec` (`given`/`when`/`then` BDD blocks, often `Given`/`When`/`Then`). You typically put your test bodies inside an `init { }` block (StringSpec/BehaviorSpec) or directly in lambdas (FunSpec/DescribeSpec). All styles share the same matchers, lifecycle callbacks, and config — only the layout differs, so the choice is about readability/team convention, not capability.

go deeper

for a junior

Can name the styles and state that the base class chooses the layout, not an annotation.

for a middle

Explains init-block vs constructor-lambda registration and that all styles share engine/matchers.

for a senior

Discusses per-class choice, team conventions, and mapping styles to test types (unit vs BDD acceptance).

for a principal

Frames style selection as a consistency/maintainability governance decision and standardizes it across the codebase.

## The core idea Unlike JUnit, Kotest does not mark tests with an annotation like `@Test`. Instead **the test layout/style is selected by the base class your test class extends**. Switching `class MyTest : StringSpec()` to `class MyTest : FunSpec()` changes the DSL you write tests with. All styles compile to the same engine and support the same matchers, lifecycle hooks, and per-test config — only the *syntax for declaring and nesting tests* differs. ## The four common styles - **StringSpec** — the most minimal. A string literal is the test name, followed by a lambda body. No nesting. - **FunSpec** — tests are declared with `test("name") { }`; group with `context("name") { }`. - **DescribeSpec** — RSpec/Jest-like: `describe("...") { it("...") { } }`, freely nestable. - **BehaviorSpec** — BDD: `given("...") { `when`("...") { then("...") { } } }` (capitalized `Given`/`When`/`Then` aliases exist to avoid the `when` keyword backtick). ## Where the test bodies go Some styles declare tests in an `init { }` block (StringSpec, BehaviorSpec, WordSpec); others (FunSpec, DescribeSpec) accept a lambda passed to the constructor. Both are valid — Kotest registers tests at construction time. ```kotlin import io.kotest.core.spec.style.StringSpec import io.kotest.core.spec.style.FunSpec import io.kotest.matchers.shouldBe class StringStyle : StringSpec({ "1 + 1 should be 2" { (1 + 1) shouldBe 2 } }) class FunStyle : FunSpec({ context("addition") { test("1 + 1 is 2") { (1 + 1) shouldBe 2 } } }) ``` ## Why it matters The spec style is a **per-class** decision; different test classes in the same project can use different styles. Teams usually standardize on one or two (e.g. FunSpec for unit, BehaviorSpec for BDD acceptance tests) for consistency.

  • Can two test classes in the same module use different spec styles?
    Yes. Style is a per-class choice driven by the base class, so you can freely mix StringSpec and BehaviorSpec across files.
  • Does the spec style affect which matchers you can use?
    No. Matchers (e.g. shouldBe) are independent of style; every style uses the same matcher library.

Choosing a spec style is like choosing a notebook template: lined, dotted, or grid — the paper (engine) is the same; only the layout you write on differs.

saying these in an interview costs you the question

  • Claiming Kotest tests use a @Test annotation like JUnit
  • Saying the style determines which assertions/matchers are available
  • Thinking you must use one style across the whole project
  • Confusing spec style with the test framework choice itself

context

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

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

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