Kotest offers beforeTest, beforeEach, beforeContainer and beforeAny. In a nested spec where tests live inside a context block, which of these fires for which test cases?
answer
- container blocks are TestCases too
- Each = leaves, Container = containers, Any/Test = both
- beforeAny is an alias of beforeTest
- flat spec hides the difference until nesting appears
- beforeContainer fires at every depth
basics
~10 sKotest treats containers and leaves both as test cases. beforeEach/afterEach fire only for leaf tests; beforeContainer/afterContainer only for containers; beforeTest/afterTest and their aliases beforeAny/afterAny fire for both.
solid answer
~50 sKotest models every node in a spec — a `context`/`describe`/`given` block as well as an actual test — as a `TestCase`. The hook family splits along that line: - `beforeEach` / `afterEach` — leaf test cases only. - `beforeContainer` / `afterContainer` — container test cases only. - `beforeTest` / `afterTest` — every test case, container or leaf. - `beforeAny` / `afterAny` — the same coverage as beforeTest/afterTest; they exist so the naming reads symmetrically next to Each/Container. So in `context("orders") { test("create"){}; test("cancel"){} }`, `beforeContainer` runs once, `beforeEach` runs twice, and `beforeTest` runs three times. The classic bug: in a **flat** spec every test is a leaf, so `beforeTest` and `beforeEach` look identical; someone later wraps tests in a context and the `beforeTest` fixture suddenly also runs for the container, doubling side effects or resetting state at the wrong granularity. Pick `beforeEach` when you mean "before each real test".
code
kotlin · 10 linesclass NestedHooksTest : FunSpec({
beforeTest { println("beforeTest") } // 3x: context + 2 tests
beforeContainer { println("beforeContainer") } // 1x: the context
beforeEach { println("beforeEach") } // 2x: the two tests
context("orders") {
test("create") { }
test("cancel") { }
}
})go deeper
Know that Each means leaves and Container means container blocks, and that beforeTest covers both.
State the full matrix with counts for a concrete nested example and name beforeAny as the alias of beforeTest.
Lead with the diagnosis: flat-to-nested migration silently changes how often a beforeTest fixture runs; pick hooks by intent.
Turn it into a convention — per-test fixtures use Each, group fixtures live inline in the container, and Test/Any is reserved for cross-cutting instrumentation.
## Containers are test cases too Kotest's execution model has one node type: the `TestCase`. A `context("…") { }` in FunSpec, a `describe` in DescribeSpec, a `given`/`when` in BehaviorSpec, a `"…" should { }` in WordSpec — all of these are test cases that happen to contain other test cases. A `test("…") { }` with nothing nested inside it is a leaf. Everything about Kotest's hook taxonomy follows from that single fact: the hooks differ in *which kind of test case* they fire for. ## The matrix | Hook | Fires for containers | Fires for leaves | |---|---|---| | `beforeContainer` / `afterContainer` | yes | no | | `beforeEach` / `afterEach` | no | yes | | `beforeTest` / `afterTest` | yes | yes | | `beforeAny` / `afterAny` | yes | yes | `beforeAny`/`afterAny` cover exactly the same set as `beforeTest`/`afterTest`. They exist so that the trio Each / Container / Any reads consistently; there is no behavioural distinction to memorise beyond "Any means both kinds". The `after…` variants mirror the `before…` ones and, like `afterTest`, are handed the result alongside the test case. ## Worked example ```kotlin class NestedHooksTest : FunSpec({ beforeTest { println("beforeTest") } beforeContainer { println("beforeContainer") } beforeEach { println("beforeEach") } context("orders") { test("create") { } test("cancel") { } } }) ``` Three test cases exist: the container `orders` and the two leaves. Output counts: `beforeContainer` once, `beforeEach` twice, `beforeTest` three times — once for the container and once per leaf. The container's own callback fires before the leaves beneath it run, because the container is entered first and its children execute inside it. ## The failure mode this question is really about Most suites start flat: ```kotlin class UserServiceTest : FunSpec({ beforeTest { db.truncateAll() } test("a") { } test("b") { } }) ``` Here every test case is a leaf, so `beforeTest` and `beforeEach` are indistinguishable and nobody notices which one they picked. Later somebody groups the tests: ```kotlin context("when the user exists") { beforeTestSeedsRanHere() // conceptually test("a") { } } ``` Now `beforeTest` also fires for `when the user exists`. Two symptoms follow: 1. **Duplicated side effects.** Truncating, seeding, inserting, incrementing or starting something now happens once extra per container. Counters and "exactly one row" assertions break. 2. **Wrong-granularity teardown.** The `afterTest` counterpart fires when the *container* finishes as well, so a cleanup you meant to run "after each test" also runs after the whole group — occasionally after state that later tests in a sibling group depended on. The fix is to say what you mean: `beforeEach`/`afterEach` for per-test fixtures, `beforeContainer`/`afterContainer` for setup that belongs to a group (seed data shared by everything inside that context), `beforeTest`/`beforeAny` only when you genuinely want both — cross-cutting instrumentation such as logging or timing every node. ## Interaction with nesting depth These hooks are registered per spec, not per container, so `beforeContainer` fires for **every** container in the spec, at any depth — not just top-level ones. If you have contexts inside contexts, a `beforeContainer` block runs for each of them, outermost first as the engine descends. If you need setup scoped to one particular group, prefer writing the setup code directly inside that container block, where it runs exactly once for that subtree, rather than a spec-wide `beforeContainer` that has to branch on the test case's identity. Be careful with setup written directly inside a container body, though: whether that code re-runs depends on the spec's isolation mode, since the container path is re-executed when Kotest creates fresh spec instances. ## Overridable equivalents Each of these has a matching overridable member function on the spec (`override suspend fun beforeEach(testCase: TestCase)`, `beforeContainer`, `beforeAny`, and so on), which is how a shared base spec applies the same rule to many specs. Both mechanisms are live at once: an inherited override and a locally declared block both fire. ## What interviewers listen for The crisp statement that containers are test cases, the Each/Container/Any split, that Any is an alias of the Test pair, and — the senior tell — the migration hazard when a flat spec grows nesting.
- Someone reports that their per-test database reset started running one extra time after they wrapped tests in a context block. What happened and how do you fix it?Their hook is `beforeTest` (or `beforeAny`), which fires for container test cases as well as leaves, so wrapping the tests in a context added one more firing for the container itself. Switching the hook to `beforeEach` restricts it to leaf tests and restores the old count. If the reset genuinely belongs to the group, `beforeContainer` is the right home instead.
- How would you scope setup to just one context in a spec rather than to all containers?Write the setup inline at the top of that container's body — it executes once when the engine enters that container, which is exactly the scope you want, and needs no branching on names. A spec-wide `beforeContainer` fires for every container at every depth, so scoping it would require inspecting the TestCase, which is brittle. Remember that inline container setup re-executes when the isolation mode creates fresh spec instances per leaf.
saying these in an interview costs you the question
- Saying beforeTest fires only for leaf tests
- Claiming beforeAny has different coverage from beforeTest
- Assuming beforeContainer only fires for top-level containers
- Thinking the container/leaf distinction depends on the spec style rather than on whether the node has children
- Using beforeTest for per-test fixtures in a nested spec and being surprised by doubled side effects