| name | vitest-testing |
| description | Vitest testing patterns for production TypeScript projects. Use when writing tests, fixing test failures, configuring coverage, mocking modules, debugging flaky tests, or setting up Vitest in a monorepo. Covers Vitest 3+, vi.mock, vi.fn, React Testing Library, PGlite in-memory PostgreSQL, pool forks, maxWorkers, and hookTimeout. |
| license | MIT |
| allowed-tools | Read, Grep, Glob |
| metadata | {"author":"RevealUI Studio","version":"0.1.0","website":"https://revealui.com"} |
Vitest Testing Patterns
Test File Conventions
- Unit/integration tests:
*.test.ts or *.test.tsx (co-located with source)
- Test directories:
__tests__/ for grouped tests
- Fixtures:
__fixtures__/ or test/fixtures/
- Test utilities: shared test package or
test/helpers.ts
Writing Tests
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
describe('MyFunction', () => {
it('should handle the happy path', () => {
const result = myFunction('valid-input');
expect(result).toEqual({ status: 'ok' });
});
it('should throw on invalid input', () => {
expect(() => myFunction('')).toThrow('Input required');
});
});
Mocking
vi.mock('./database', () => ({
query: vi.fn().mockResolvedValue([{ id: 1 }]),
}));
vi.mock('./service', () => ({
MyService: class {
async getData() { return []; }
},
}));
const spy = vi.spyOn(object, 'method');
spy.mockReturnValue('mocked');
Important: When using Biome, always mock classes with class syntax inside vi.mock() factories. Biome converts function() to arrow functions, which breaks new.
Async Testing
it('should fetch data', async () => {
const result = await fetchData();
expect(result).toHaveLength(3);
});
it('should reject on error', async () => {
await expect(fetchBadData()).rejects.toThrow('Not found');
});
PGlite (In-Memory PostgreSQL)
import { PGlite } from '@electric-sql/pglite';
let db: PGlite;
beforeEach(async () => {
db = new PGlite();
await db.exec(schema);
});
afterEach(async () => {
await db.close();
});
Config for PGlite: PGlite startup can take 3-5s under load. Set hookTimeout: 30000 in vitest.config.ts:
export default defineConfig({
test: {
hookTimeout: 30000,
},
});
Monorepo Testing
export default defineConfig({
test: {
pool: 'forks',
maxWorkers: 2,
hookTimeout: 30000,
include: ['src/**/*.test.{ts,tsx}'],
},
});
- Use
pool: 'forks' for process isolation (prevents shared state leaks)
- Cap
maxWorkers to prevent fork explosion (turbo concurrency x workers = total processes)
- Disable test caching in turbo (
"cache": false in turbo.json test task) to prevent stale results
Coverage
export default defineConfig({
test: {
coverage: {
provider: 'v8',
reporter: ['text', 'lcov'],
exclude: ['**/*.test.ts', '**/__fixtures__/**'],
thresholds: {
statements: 80,
branches: 75,
functions: 80,
lines: 80,
},
},
},
});
Common Mistakes to Avoid
- Don't use
jest.fn() — use vi.fn() (Vitest, not Jest)
- Don't mock everything — prefer real implementations, mock only external services
- Don't use
function() in vi.mock() factories — Biome converts to arrows, breaking new
- Don't share state between tests — use
beforeEach to reset
- Don't use threads pool with native modules — use
forks
- Don't set
hookTimeout too low when using PGlite
Debugging Flaky Tests
- Check for shared mutable state between tests
- Check for timing issues (
setTimeout, setInterval)
- Check for port conflicts (parallel workers binding same port)
- Check for PGlite startup timeout under load
- Run with
--reporter=verbose to see individual test timings
- Run single test:
vitest run src/path/to/file.test.ts
Skill by RevealUI Studio — the agentic business runtime.