Files
agent-skills/skills/vitest/references/core-hooks.md
Jason Woltje f5792c40be feat: Complete fleet — 94 skills across 10+ domains
Pulled ALL skills from 15 source repositories:
- anthropics/skills: 16 (docs, design, MCP, testing)
- obra/superpowers: 14 (TDD, debugging, agents, planning)
- coreyhaines31/marketingskills: 25 (marketing, CRO, SEO, growth)
- better-auth/skills: 5 (auth patterns)
- vercel-labs/agent-skills: 5 (React, design, Vercel)
- antfu/skills: 16 (Vue, Vite, Vitest, pnpm, Turborepo)
- Plus 13 individual skills from various repos

Mosaic Stack is not limited to coding — the Orchestrator and
subagents serve coding, business, design, marketing, writing,
logistics, analysis, and more.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 16:27:42 -06:00

4.4 KiB

name, description
name description
lifecycle-hooks beforeEach, afterEach, beforeAll, afterAll, and around hooks

Lifecycle Hooks

Basic Hooks

import { afterAll, afterEach, beforeAll, beforeEach, test } from 'vitest'

beforeAll(async () => {
  // Runs once before all tests in file/suite
  await setupDatabase()
})

afterAll(async () => {
  // Runs once after all tests in file/suite
  await teardownDatabase()
})

beforeEach(async () => {
  // Runs before each test
  await clearTestData()
})

afterEach(async () => {
  // Runs after each test
  await cleanupMocks()
})

Cleanup Return Pattern

Return cleanup function from before* hooks:

beforeAll(async () => {
  const server = await startServer()
  
  // Returned function runs as afterAll
  return async () => {
    await server.close()
  }
})

beforeEach(async () => {
  const connection = await connect()
  
  // Runs as afterEach
  return () => connection.close()
})

Scoped Hooks

Hooks apply to current suite and nested suites:

describe('outer', () => {
  beforeEach(() => console.log('outer before'))
  
  test('test 1', () => {}) // outer before → test
  
  describe('inner', () => {
    beforeEach(() => console.log('inner before'))
    
    test('test 2', () => {}) // outer before → inner before → test
  })
})

Hook Timeout

beforeAll(async () => {
  await slowSetup()
}, 30_000) // 30 second timeout

Around Hooks

Wrap tests with setup/teardown context:

import { aroundEach, test } from 'vitest'

// Wrap each test in database transaction
aroundEach(async (runTest) => {
  await db.beginTransaction()
  await runTest() // Must be called!
  await db.rollback()
})

test('insert user', async () => {
  await db.insert({ name: 'Alice' })
  // Automatically rolled back after test
})

aroundAll

Wrap entire suite:

import { aroundAll, test } from 'vitest'

aroundAll(async (runSuite) => {
  console.log('before all tests')
  await runSuite() // Must be called!
  console.log('after all tests')
})

Multiple Around Hooks

Nested like onion layers:

aroundEach(async (runTest) => {
  console.log('outer before')
  await runTest()
  console.log('outer after')
})

aroundEach(async (runTest) => {
  console.log('inner before')
  await runTest()
  console.log('inner after')
})

// Order: outer before → inner before → test → inner after → outer after

Test Hooks

Inside test body:

import { onTestFailed, onTestFinished, test } from 'vitest'

test('with cleanup', () => {
  const db = connect()
  
  // Runs after test finishes (pass or fail)
  onTestFinished(() => db.close())
  
  // Only runs if test fails
  onTestFailed(({ task }) => {
    console.log('Failed:', task.result?.errors)
  })
  
  db.query('SELECT * FROM users')
})

Reusable Cleanup Pattern

function useTestDb() {
  const db = connect()
  onTestFinished(() => db.close())
  return db
}

test('query users', () => {
  const db = useTestDb()
  expect(db.query('SELECT * FROM users')).toBeDefined()
})

test('query orders', () => {
  const db = useTestDb() // Fresh connection, auto-closed
  expect(db.query('SELECT * FROM orders')).toBeDefined()
})

Concurrent Test Hooks

For concurrent tests, use context's hooks:

test.concurrent('concurrent', ({ onTestFinished }) => {
  const resource = allocate()
  onTestFinished(() => resource.release())
})

Extended Test Hooks

With test.extend, hooks are type-aware:

const test = base.extend<{ db: Database }>({
  db: async ({}, use) => {
    const db = await createDb()
    await use(db)
    await db.close()
  },
})

// These hooks know about `db` fixture
test.beforeEach(({ db }) => {
  db.seed()
})

test.afterEach(({ db }) => {
  db.clear()
})

Hook Execution Order

Default order (stack):

  1. beforeAll (in order)
  2. beforeEach (in order)
  3. Test
  4. afterEach (reverse order)
  5. afterAll (reverse order)

Configure with sequence.hooks:

defineConfig({
  test: {
    sequence: {
      hooks: 'list', // 'stack' (default), 'list', 'parallel'
    },
  },
})

Key Points

  • Hooks are not called during type checking
  • Return cleanup function from before* to avoid after* duplication
  • aroundEach/aroundAll must call runTest()/runSuite()
  • onTestFinished always runs, even if test fails
  • Use context hooks for concurrent tests