skip to content

How does Kotest's FeatureSpec structure a test file, and how does its nesting differ from Kotest's BehaviorSpec?

level: middleimportance: nice to knowfreq 25%

answer

  1. feature = container, scenario = leaf
  2. features nest inside features - open depth
  3. Feature: / Scenario: name prefixes
  4. xfeature / xscenario disable
  5. BehaviorSpec = fixed roles, FeatureSpec = free depth

basics

~20 s

FeatureSpec uses feature(...) containers holding scenario(...) leaf tests, with names rendered as Feature: / Scenario:. Features may nest inside features for arbitrary depth, whereas BehaviorSpec has a fixed given/when/then vocabulary. Disabled variants are xfeature and xscenario.

solid answer

~50 s

A Kotest FeatureSpec reads like a feature document expressed in Kotlin: feature("...") { scenario("...") { } }. feature is a container, scenario is the leaf test that carries assertions, and displayed names are prefixed Feature: and Scenario:. The structural difference from BehaviorSpec is vocabulary versus depth. BehaviorSpec gives three named roles - given, when, then - plus and, so the shape of a spec is largely dictated by the style. FeatureSpec has one container word that can nest inside itself, so you can group a feature into sub-features to whatever depth reads well, but the style says nothing about arrange/act/assert; that discipline is on you. Both are BDD-flavoured and both have x-prefixed disabled twins (xfeature, xscenario, xgiven, xwhen, xthen). Choose FeatureSpec when your suite mirrors product features, BehaviorSpec when the given/when/then narrative is the value.

code

kotlin · 13 lines
kotlin
class CheckoutSpec : FeatureSpec({
    feature("checkout") {
        scenario("totals an empty basket as zero") {
            Basket().total() shouldBe 0
        }
        feature("discounts") {
            scenario("applies a valid code") {
                Basket(1000).withCode("TEN").total() shouldBe 900
            }
            xscenario("stacks two codes") { /* pending */ }
        }
    }
})

go deeper

for a junior

Recall the shape: feature contains scenario, scenario is the test, x-prefix disables.

for a middle

Contrast the fixed BehaviorSpec vocabulary with FeatureSpec's self-nesting single container, and mention the name prefixes.

for a senior

Talk about nesting discipline, what disabling a container does to unregistered children, and when a feature-shaped suite earns the style.

for a principal

Position it as a documentation contract: which dialect matches how the organisation writes requirements, and how deep grouping is allowed to go.

## The FeatureSpec shape FeatureSpec models a specification the way an acceptance-test document does: a feature, and inside it the scenarios that demonstrate it. - feature("checkout") { ... } - a container. Its body executes and registers whatever it declares. - scenario("applies a discount code") { ... } - a leaf test. Assertions go here. Rendered names carry the keyword prefix, so a failure reads Feature: checkout / Scenario: applies a discount code. As with any Kotest style, do not repeat the keyword in the string. ## Nesting model A feature block may contain further feature blocks as well as scenarios, so depth is open-ended with a single container word. That is the main structural contrast with BehaviorSpec, which offers three role-named levels (given, when, then) plus and, and whose leaf is always then. Put differently: BehaviorSpec constrains you into a narrative; FeatureSpec gives you a tree and lets you decide what each level means. The practical fallout is naming discipline. Because every level of a FeatureSpec uses the same word, a deep file can turn into feature-inside-feature-inside-feature where the reader cannot tell which level is the subject and which is the condition. Teams that pick FeatureSpec usually cap nesting at two feature levels and let the scenario sentence carry the rest. ## Disabling Both blocks have disabled variants: xfeature and xscenario. Prefixing a container with x means the engine marks that node disabled and does not execute its body, so nothing nested inside it registers or runs; only the disabled container is reported as ignored. That is the same mechanism as xgiven/xwhen/xthen in BehaviorSpec. ## Choosing between the two BDD styles Use FeatureSpec when the suite is organised around product features, when you are transcribing existing feature documents, or when you want free grouping depth. Use BehaviorSpec when the value is the given/when/then narrative itself - it forces every test to name its precondition, its action and its expectation, which is exactly what you want when the specs double as documentation for non-engineers. Both compile to the same container/leaf tree and run on the same engine, so the choice is about readability and about which vocabulary your team already uses in tickets. Mixing them in one module works technically, but a reader scanning a package benefits from one BDD dialect. ## Things that trip people up A scenario is a leaf: you cannot nest a scenario inside a scenario. If you find yourself wanting to, you want a nested feature. And since container bodies execute, fixture code written directly in a feature body runs for each execution of that feature - fine for cheap setup, worth hoisting or making lazy when it is not.

  • Can a scenario contain nested tests?
    No. scenario registers a leaf test, so there is no scope inside it offering further builders. Extra structure comes from nesting another feature container above the scenarios, which is allowed to any depth.

saying these in an interview costs you the question

  • Claiming FeatureSpec parses external .feature files
  • Trying to nest a scenario inside a scenario
  • Saying FeatureSpec has given/when/then blocks
  • Assuming xfeature still reports its nested scenarios individually as skipped

context