5. Hooks
| Hook | Signature | Scope | Notes |
|---|---|---|---|
beforeAll(fn, timeout?) |
fn: (ctx: ModuleContext) => unknown |
File (top-level) or enclosing describe |
Runs once before all tests in scope. |
afterAll(fn, timeout?) |
same | same | Runs once after all tests in scope. |
beforeEach(fn, timeout?) |
fn: (ctx: TestContext) => unknown |
File or enclosing describe |
Runs before every test in scope; may return a cleanup function, which runs after all afterEach hooks in that scope (i.e. later than ordinary afterEach). |
afterEach(fn, timeout?) |
same | same | Runs after every test in scope. |
onTestFinished(fn) |
fn: (ctx: TestContext) => unknown, called from inside a running test/hook |
Single test instance | Runs after that test’s afterEach chain; multiple registrations run in reverse registration order; not affected by sequence.hooks config. |
onTestFailed(fn) |
same shape | Single test instance | Only fires if the test failed; also runs after afterEach. |
onTestFinished and onTestFailed have two call forms
Section titled “onTestFinished and onTestFailed have two call forms”Both are destructurable off
TestContext (as shown in §3) and importable directly from 'vitest' as named exports, the same
shape as beforeEach/afterEach. Both forms throw if called outside a running test body. The
docs explicitly warn the global/imported form is unsound under .concurrent: “If you are running
tests concurrently, you should always use the onTestFailed/onTestFinished hook from the test
context since Vitest doesn’t track concurrent tests in global hooks”, i.e. the imported form
resolves “the current test” through shared module state that a single sequential runner keeps
consistent but that concurrent execution does not. An extractor doing name-based import resolution
for 'vitest' exports should recognize onTestFinished/onTestFailed as legal top-level imports,
not just as context members.
Nesting and ordering
Section titled “Nesting and ordering”- Hooks registered at file top-level apply to every test in the file; hooks registered inside a
describeapply only to tests (and nested describes) within that block. - Default execution order is a “stack”:
beforeAll/beforeEachrun outer-to-inner (parent before child),afterAll/afterEachrun inner-to-outer (child before parent), i.e.afterEachorder is the reverse ofbeforeEachregistration order, configurable globally viasequence.hooks. test.beforeEach/test.afterEach/test.beforeAll/test.afterAll(hooks hung off atest.extend()-produced object) are the same hooks, scoped to whatever suite they’re declared in, just spelled through the extended-test namespace so they can see fixture types.test.aroundEach/test.aroundAll(v4.1+) combine setup+teardown in one wrapper; exact signature UNVERIFIED, treat as an opaque hook-registering call for extraction purposes (record its presence and scope; don’t try to model control flow inside it yet).
The effective hook chain
Section titled “The effective hook chain”For context-compatibility purposes (the redundant-hook and context-equivalence rules), a
TestCase’s effective hook chain is: every
beforeAll/beforeEach from the root of the file down through each enclosing describe, in
declaration order, plus the corresponding afterEach/afterAll from each level in reverse,
this is exactly the ordered set the analyzer needs to compare for “equivalent context.”
Back to the Vitest surface index.