Vitest Configuration & Setup
Establishing deterministic test execution requires explicit configuration boundaries. This guide details production-grade Vitest setup for modern JavaScript architectures, focusing on environment isolation, mock interception, and CI pipeline enforcement.
Core Configuration Architecture & Dependency Resolution
Establishing a robust Component & Integration Testing Frameworks baseline requires explicit environment targeting. Configure vitest.config.ts to isolate unit and integration scopes, preventing cross-contamination of mock registries.
Implementation Patterns
- Initialize
vitest.config.tsusingdefineConfigfor type safety and explicit environment routing. - Set
test.environmenttojsdomfor DOM-heavy suites ornodefor pure logic. Avoid implicit defaults. - Configure
resolve.aliasto bypass Vite bundling quirks and map internal paths directly to source directories. - Restrict
globals: trueexclusively to legacy migration paths. Modern codebases should importvi,describe, andexpectexplicitly to prevent namespace pollution.
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import path from 'path';
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'~': path.resolve(__dirname, './lib'),
},
},
test: {
environment: 'jsdom',
globals: false, // Enforce explicit imports for deterministic execution
include: ['src/**/*.test.{ts,tsx}', 'src/**/*.spec.{ts,tsx}'],
exclude: ['node_modules', 'dist', '.next', 'coverage'],
// CI-specific overrides handled via environment variables or CLI flags
cache: process.env.CI ? false : { dir: 'node_modules/.vite' },
},
});
CI Pipeline Rules
- Enforce strict
test.includeglob patterns to prevent accidental execution of utility files or configuration scripts. - Set
test.cache: falsein CI to guarantee deterministic runs and eliminate stale module resolution artifacts.
Debugging Workflow
- Run
vitest --inspect-brkwith a Node debugger attached for synchronous configuration evaluation and breakpoint mapping. - Use
VITE_DEBUG=1to trace module resolution failures and dependency graph construction during startup.
Framework-Specific Integration & Mocking Strategies
Aligning Vitest with modern UI libraries demands strict adherence to Testing Library Best Practices. When wiring framework adapters, prioritize explicit render wrappers over implicit globals. For server-side routing contexts, refer to Configuring Vitest for Next.js App Router to handle use client directives and server component boundaries.
Implementation Patterns
- Implement
setupFilesAfterEnvto inject global test utilities, custom matchers, and cleanup routines. - Use
vi.mockwith factory functions for dynamic module interception. Avoid hoisting static mocks that bypass runtime evaluation. - Configure
deps.inlinefor ESM-only packages that fail under Vite’s CommonJS transformation pipeline.
// vitest.setup.ts
import { cleanup } from '@testing-library/react';
import { afterEach } from 'vitest';
// Enforce DOM cleanup after every test to prevent state leakage
afterEach(() => cleanup());
// vi.mock example with dynamic factory
vi.mock('@/services/api', async (importOriginal) => {
const actual = await importOriginal<typeof import('@/services/api')>();
return {
...actual,
fetchUser: vi.fn().mockResolvedValue({ id: 'test-1', name: 'Mock User' }),
};
});
// vitest.config.ts (extension)
export default defineConfig({
test: {
setupFilesAfterEnv: ['./vitest.setup.ts'],
deps: {
inline: ['some-esm-only-package'], // Force Vite to process as ESM
},
},
});
CI Pipeline Rules
- Block PR merges if
coverage.thresholdsfall below 80% for critical paths. Configure thresholds per directory to enforce architectural boundaries. - Cache
node_modules/.viteacross pipeline runs to reduce cold start latency without compromising test determinism.
Debugging Workflow
- Leverage
vitest --reporter=verboseto isolate failing component trees and trace assertion failures to specific render cycles. - Apply
console.trace()withinvi.spyOncallbacks to map invocation chains and verify dependency injection order.
CI Enforcement, Parallelization & Reliability Tradeoffs
Scaling execution across distributed runners introduces tradeoffs between isolation overhead and throughput. While Vitest excels at rapid feedback loops, complex DOM-heavy suites may require offloading to Playwright Component Testing for realistic browser rendering guarantees. Implement shard-aware coverage aggregation to maintain accurate metrics across parallelized jobs.
Implementation Patterns
- Enable
pool: 'forks'for CPU-bound test suites to bypass thread pool limitations and leverage full core utilization. - Implement
test.sequentialfor stateful integration flows that require strict execution ordering to prevent race conditions. - Use
test.retry: 2sparingly for known network-dependent mocks. Do not apply globally; it masks underlying instability.
// vitest.config.ts (production CI profile)
export default defineConfig({
test: {
pool: 'forks',
poolOptions: {
forks: {
singleFork: false,
execArgv: ['--max-old-space-size=4096'],
},
},
sequence: {
shuffle: true, // Enforce non-deterministic ordering locally to catch hidden dependencies
},
},
});
// Example of sequential execution for stateful tests
import { describe, it, expect } from 'vitest';
describe('Stateful Integration Flow', () => {
it.sequential('initializes database connection', async () => { /* ... */ });
it.sequential('executes migration batch', async () => { /* ... */ });
it.sequential('verifies schema integrity', async () => { /* ... */ });
});
CI Pipeline Rules
- Enforce
--maxWorkers=autowith explicit memory caps (--max-old-space-size=4096) to prevent OOM kills during parallel execution. - Fail builds on
test.watchartifacts or leftover__snapshots__drift. Runvitest --run --updateonly in controlled release branches.
Debugging Workflow
- Capture
vitest --run --reporter=jsonoutput for CI log parsing and automated failure triage. - Profile test execution with
--bail=1to halt on first regression and isolate the exact failure vector before cascading timeouts occur.
Production CI Configuration
# .github/workflows/vitest.yml
name: Vitest CI Enforcement
on:
pull_request:
branches: [main, develop]
jobs:
vitest:
runs-on: ubuntu-latest
strategy:
matrix:
shard: [1, 2, 3]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- run: npm ci
- name: Run Tests with Coverage
run: npx vitest --run --coverage --shard=${{ matrix.shard }}/3
env:
CI: true
VITE_DEBUG: 1
- name: Enforce Coverage Thresholds
run: npx vitest --run --coverage --coverage.thresholds.lines=80 --coverage.thresholds.branches=80
- name: Upload Coverage Artifacts
uses: actions/upload-artifact@v4
with:
name: coverage-${{ matrix.shard }}
path: coverage/