Do Kotest's different spec styles behave differently at runtime, or are they only syntax? What actually differs when you pick one?
answer
- all styles produce the same container/leaf tree
- matchers/extensions/isolation/reporting are style-independent
- differs: vocabulary, expressible nesting, name derivation
- AnnotationSpec = structural outlier (method names, no containers)
- mixing styles compiles; consistency is a human argument
basics
~20 sThey are different DSLs over the same engine: every style compiles to the same container/leaf test tree, with the same matchers, extensions, configuration and reporting. What genuinely differs is the vocabulary, the shapes you can express (nesting or not), and how displayed names are derived.
solid answer
~50 sEach Kotest style is a base class exposing a different set of builders; all of them register the same kind of nodes into the same tree, executed by the same engine. Matchers, extensions and listeners, isolation and lifecycle configuration, reporting and filtering are style-independent. Three things really differ. Vocabulary: the words you write and therefore the story the file tells. Expressible shape: whether containers exist at all, and how deep nesting can go - a flat style simply cannot express a two-level grouping or a fixture scoped to a subset. Name derivation: several styles prefix their keyword when rendering, so the same tree reads differently in a report; AnnotationSpec goes further and takes names from method identifiers instead of strings. So mixing styles in one module compiles and runs fine - there is no technical coupling. Consistency is a readability and review argument, not a runtime constraint.
go deeper
Say the styles are different DSLs for writing the same kind of tests; the runtime and matchers are the same.
Name what actually differs: vocabulary, whether containers exist, and how names are rendered.
Separate capability differences (containers, naming) from taste, and explain why mixing works technically but costs readers.
Set the policy: a default style, an explicit exception rule per test category, and expressibility - not preference - as the trigger for deviating.
## The common runtime A Kotest spec, whatever the style, becomes a tree of test cases with container nodes and leaf nodes. The engine walks that tree. Everything a candidate might imagine is style-specific is not: - The matcher library. shouldBe and friends are plain functions; they do not know what style called them. - Extensions, listeners and lifecycle callbacks, which attach to specs and tests, not to a dialect. - Isolation configuration, timeouts, tags, parallelism and the rest of the configuration surface. - Reporting and filtering, which operate on the tree and on names. That is why you can change a spec from one DSL style to another and expect the same tests to pass, and why a module containing several styles has no integration problem. ## What genuinely differs 1. Vocabulary. The builder names - test/context, describe/it, given/when/then, feature/scenario, should, expect, or a bare string - set the register the file speaks in. This is the whole point of having ten styles: match the words to how the team describes behaviour. 2. Expressible shape. Some styles are flat: tests are registered directly on the spec and there is no container level, so a two-level grouping simply cannot be written. Nested styles supply containers, and the BDD styles constrain what those containers mean (a fixed given/when/then narrative in BehaviorSpec) or leave them free (self-nesting features in FeatureSpec). If you need to scope a fixture to a subset of tests, you need a style with containers - that is a capability difference, not taste. 3. Name derivation. The rendered name is not always just your string: BDD styles prefix Given:/When:/Then: or Feature:/Scenario:. AnnotationSpec is the outlier that takes the name from the method identifier and offers no containers at all - the one style whose limits are structural rather than stylistic. ## What that means for a consistency policy Since nothing technical forces one style, the argument for consistency is human: a reader moving between files should not have to re-learn the dialect, review comments about structure become comparable, and shared test helpers written against one style's scope types stay reusable. The counter-argument is also real - an acceptance-level suite and a table-driven unit suite genuinely want different vocabularies. A defensible middle position is to fix a default style for the bulk of the suite, allow a named exception per test category with a stated reason, and keep style choice out of per-author taste. Anything you cannot express in the default - notably scoped fixtures if the default is flat - is a legitimate trigger for the exception. ## Where candidates go wrong The common mistake is asserting that one style is 'more powerful' generally, or that isolation, parallelism or matchers behave differently per style. They do not. The precise claim is narrower and more convincing: styles differ in vocabulary, in whether containers exist, and in how names render; everything downstream of the tree is shared.
- Name one difference between styles that is a genuine capability limit rather than taste.Whether the style has containers. A flat style cannot express a grouping level, so it cannot scope a fixture to a subset of the file's tests or produce a nested report tree - you fall back to spec-level setup or helper functions. AnnotationSpec goes further and also gives up free-text names.
- If styles are interchangeable at runtime, why standardize at all?For readability and reviewability: a reader should learn one dialect, structural review comments should be comparable across files, and shared helpers written against one style's scope types stay reusable. It is a human-cost argument, which is why a well-argued exception for a genuinely different test category is reasonable.
saying these in an interview costs you the question
- Claiming isolation, parallelism or matcher behaviour differs by style
- Saying one style is generically 'more powerful' without naming containers or naming
- Believing two styles cannot coexist in a module for technical reasons
- Forgetting AnnotationSpec's structural limits when generalising 'styles are just syntax'