Loading source
Pulling the file list, source metadata, and syntax-aware rendering for this listing.
Source from repo
Vitest 3.x reference skill covering configuration, test/describe APIs, mocking, coverage, snapshots, and concurrency.
Files
Skill
Size
Entrypoint
Format
Open file
Syntax-highlighted preview of this file as included in the skill package.
references/core-hooks.md
1---2name: lifecycle-hooks3description: beforeEach, afterEach, beforeAll, afterAll, and around hooks4---56# Lifecycle Hooks78## Basic Hooks910```ts11import { afterAll, afterEach, beforeAll, beforeEach, test } from 'vitest'1213beforeAll(async () => {14// Runs once before all tests in file/suite15await setupDatabase()16})1718afterAll(async () => {19// Runs once after all tests in file/suite20await teardownDatabase()21})2223beforeEach(async () => {24// Runs before each test25await clearTestData()26})2728afterEach(async () => {29// Runs after each test30await cleanupMocks()31})32```3334## Cleanup Return Pattern3536Return cleanup function from `before*` hooks:3738```ts39beforeAll(async () => {40const server = await startServer()4142// Returned function runs as afterAll43return async () => {44await server.close()45}46})4748beforeEach(async () => {49const connection = await connect()5051// Runs as afterEach52return () => connection.close()53})54```5556## Scoped Hooks5758Hooks apply to current suite and nested suites:5960```ts61describe('outer', () => {62beforeEach(() => console.log('outer before'))6364test('test 1', () => {}) // outer before → test6566describe('inner', () => {67beforeEach(() => console.log('inner before'))6869test('test 2', () => {}) // outer before → inner before → test70})71})72```7374## Hook Timeout7576```ts77beforeAll(async () => {78await slowSetup()79}, 30_000) // 30 second timeout80```8182## Around Hooks8384Wrap tests with setup/teardown context:8586```ts87import { aroundEach, test } from 'vitest'8889// Wrap each test in database transaction90aroundEach(async (runTest) => {91await db.beginTransaction()92await runTest() // Must be called!93await db.rollback()94})9596test('insert user', async () => {97await db.insert({ name: 'Alice' })98// Automatically rolled back after test99})100```101102### aroundAll103104Wrap entire suite:105106```ts107import { aroundAll, test } from 'vitest'108109aroundAll(async (runSuite) => {110console.log('before all tests')111await runSuite() // Must be called!112console.log('after all tests')113})114```115116### Multiple Around Hooks117118Nested like onion layers:119120```ts121aroundEach(async (runTest) => {122console.log('outer before')123await runTest()124console.log('outer after')125})126127aroundEach(async (runTest) => {128console.log('inner before')129await runTest()130console.log('inner after')131})132133// Order: outer before → inner before → test → inner after → outer after134```135136## Test Hooks137138Inside test body:139140```ts141import { onTestFailed, onTestFinished, test } from 'vitest'142143test('with cleanup', () => {144const db = connect()145146// Runs after test finishes (pass or fail)147onTestFinished(() => db.close())148149// Only runs if test fails150onTestFailed(({ task }) => {151console.log('Failed:', task.result?.errors)152})153154db.query('SELECT * FROM users')155})156```157158### Reusable Cleanup Pattern159160```ts161function useTestDb() {162const db = connect()163onTestFinished(() => db.close())164return db165}166167test('query users', () => {168const db = useTestDb()169expect(db.query('SELECT * FROM users')).toBeDefined()170})171172test('query orders', () => {173const db = useTestDb() // Fresh connection, auto-closed174expect(db.query('SELECT * FROM orders')).toBeDefined()175})176```177178## Concurrent Test Hooks179180For concurrent tests, use context's hooks:181182```ts183test.concurrent('concurrent', ({ onTestFinished }) => {184const resource = allocate()185onTestFinished(() => resource.release())186})187```188189## Extended Test Hooks190191With `test.extend`, hooks are type-aware:192193```ts194const test = base.extend<{ db: Database }>({195db: async ({}, use) => {196const db = await createDb()197await use(db)198await db.close()199},200})201202// These hooks know about `db` fixture203test.beforeEach(({ db }) => {204db.seed()205})206207test.afterEach(({ db }) => {208db.clear()209})210```211212## Hook Execution Order213214Default order (stack):2151. `beforeAll` (in order)2162. `beforeEach` (in order)2173. Test2184. `afterEach` (reverse order)2195. `afterAll` (reverse order)220221Configure with `sequence.hooks`:222223```ts224defineConfig({225test: {226sequence: {227hooks: 'list', // 'stack' (default), 'list', 'parallel'228},229},230})231```232233## Key Points234235- Hooks are not called during type checking236- Return cleanup function from `before*` to avoid `after*` duplication237- `aroundEach`/`aroundAll` must call `runTest()`/`runSuite()`238- `onTestFinished` always runs, even if test fails239- Use context hooks for concurrent tests240241<!--242Source references:243- https://vitest.dev/api/hooks.html244-->245