
Vitest V4
- 52 installs
- 14 repo stars
- Updated April 20, 2026
- nodnarbnitram/claude-code-extensions
Helps with testing & qa tasks during AI-assisted development.
About
vitest-v4 is a Claude Code skill for testing & qa. It helps solo builders move faster with AI-assisted coding.
- vitest-v4
- Testing & QA
- AI-coding skill
Vitest V4 by the numbers
- 52 all-time installs (skills.sh)
- Ranked #1,215 of 2,153 Testing & QA skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/nodnarbnitram/claude-code-extensions --skill vitest-v4Add your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 52 |
|---|---|
| repo stars | ★ 14 |
| Last updated | April 20, 2026 |
| Repository | nodnarbnitram/claude-code-extensions ↗ |
What it does
Helps with testing & qa tasks during AI-assisted development.
Files
Vitest 4 Testing Skill
Write, configure, and debug Vitest 4 test suites with Vite-native patterns.
Before You Start
This skill prevents 7+ common Vitest 4 mistakes and saves ~50% tokens.
| Metric | Without Skill | With Skill |
|---|---|---|
| Setup Time | ~90 min | ~30 min |
| Common Errors | 7+ | 0 |
| Token Usage | High (trial/error) | Low (known patterns) |
Known Issues This Skill Prevents
1. Hanging agent runs from using watch mode instead of vitest run 2. Broken coverage configs from using removed coverage.all or coverage.extensions 3. Browser Mode spying failures from sealed ESM namespace objects 4. Mock leakage between tests from missing restore/reset config 5. Invalid multi-project setup from using deprecated workspace terminology 6. Wrong APIs from mixing Jest helpers into Vitest tests 7. Flaky browser interactions from using synthetic helpers instead of vitest/browser 8. Slow or unstable large suites from choosing the wrong execution pool or isolation mode
Quick Start
Step 1: Configure Vitest 4 for agent-safe runs
// vitest.config.ts
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'node',
restoreMocks: true,
clearMocks: true,
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
},
});Why this matters: Vitest 4 removed coverage.all and coverage.extensions, and agent/CI environments need one-shot execution plus automatic mock cleanup.
Step 2: Write tests with Vitest APIs, not Jest APIs
import { describe, expect, it, vi } from 'vitest';
import { addUser } from './add-user';
import * as api from './api';
describe('addUser', () => {
it('returns the created user', async () => {
vi.spyOn(api, 'createUser').mockResolvedValue({ id: '1', name: 'Ada' });
await expect(addUser('Ada')).resolves.toEqual({ id: '1', name: 'Ada' });
});
});Why this matters: vi is the supported mocking API. Mixing jest.fn() or Jest-only patterns causes confusing failures and poor autocomplete.
Import rule: Importdescribe,it,expect, andvifromvitestunless the project explicitly enablesglobals: true.
Step 3: Use the correct runtime command
vitest run
vitest run --coverage
vitest run path/to/example.test.tsWhy this matters: vitest without run starts watch mode by default in development, which is a poor fit for agents, CI, and non-interactive verification.
Critical Rules
Always Do
- Use
vitest runorvitest --no-watchfor agent and CI workflows - Prefer
vi.mock(import('./module'))for type-safe module mocks - Configure
restoreMocks,clearMocks, ormockResetintentionally - Use
projectsfor multi-project configs; the rename began in Vitest 3.2 and older workspace-file usage is removed in Vitest 4 - Use a shared base config when multiple
projectsneed common settings; projects do not inherit root config unless you opt in - Use
coverage.includeto report on untested source files - Use
pageanduserEventfromvitest/browserin Browser Mode - Prefer
forkswhen native modules or runtime compatibility matter more than raw speed - Share
vitest.config.tsand the implementation file when asking AI to generate tests
Never Do
- Never use
jest.fn,jest.spyOn, or Jest-only globals in Vitest code - Never rely on removed
coverage.allorcoverage.extensionsin Vitest 4 - Never use plain watch mode for agent-driven verification
- Never use
vi.spyOnon native ESM exports in Browser Mode - Never forget that
vi.mock()is hoisted before the rest of the file executes - Never leave env/global stubs un-restored across tests
Common Mistakes
Wrong - removed coverage option:
export default defineConfig({
test: {
coverage: {
all: true,
},
},
});Correct - use include globs:
export default defineConfig({
test: {
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
},
});Why: Vitest 4 removed coverage.all and coverage.extensions; coverage.include is the supported way to include uncovered files.
Wrong - Browser Mode spy on sealed export:
import * as math from './math';
import { vi } from 'vitest';
vi.spyOn(math, 'add').mockReturnValue(10);Correct - use spy-enabled module mock:
import { vi } from 'vitest';
vi.mock(import('./math'), { spy: true });Why: Native browser ESM namespace objects are sealed, so direct spies on exports fail in Browser Mode.
Known Issues Prevention
| Issue | Root Cause | Solution |
|---|---|---|
| Tests never exit | Watch mode started in a non-interactive session | Use vitest run |
| Coverage report misses untested files | coverage.include not configured | Add explicit source globs |
| Browser Mode spy throws or does nothing | vi.spyOn used on sealed ESM exports | Use vi.mock(import('./mod'), { spy: true }) |
| Mocks leak between tests | Cleanup flags missing | Enable restoreMocks / clearMocks / unstubEnvs |
| Multi-project config breaks after upgrade | Deprecated workspace terminology or removed workspace-file patterns carried over | Switch to projects and defineProject |
| Worker or pool config stops working | Old maxThreads, maxForks, or poolOptions carried forward | Migrate to Vitest 4 worker settings such as maxWorkers |
| Project-specific config unexpectedly disappears | Root config assumptions are not inherited into projects | Use extends: true, mergeConfig, or a shared base explicitly |
| AI-generated tests use wrong helpers | Jest patterns copied into Vitest | Replace with vi, Vitest imports, and Vitest matchers |
| Browser tests hang | Blocking dialogs or wrong user-event utilities | Mock dialogs and use vitest/browser helpers |
| Fast pool causes strange native-module failures | threads chosen for a suite that needs process isolation | Switch to forks or narrow thread usage |
Bundled Resources
References
- Mocking rules and hoisting → `references/mocking-reference.md`
- Browser Mode providers and pitfalls → `references/browser-mode-reference.md`
- Coverage and multi-project config → `references/coverage-projects-reference.md`
- Pools, isolation, and persistent cache → `references/pools-execution-reference.md`
- Reference index → `references/README.md`
Configuration Reference
vitest.config.ts
import { defineConfig, defineProject } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
export default defineConfig({
test: {
projects: [
defineProject({
test: {
name: 'unit',
include: ['src/**/*.test.ts'],
environment: 'node',
},
}),
defineProject({
test: {
name: 'browser',
include: ['src/**/*.browser.test.ts'],
browser: {
enabled: true,
provider: playwright(),
instances: [{ browser: 'chromium' }],
},
},
}),
],
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
},
restoreMocks: true,
unstubEnvs: true,
setupFiles: ['./test/setup.ts'],
},
});Key settings:
test.projects: Stable multi-project terminology; the rename started in Vitest 3.2, and projects do not automatically inherit every root config value, so shared settings should be factored into a reused base when neededcoverage.include: Required when uncovered source files must appear in the reportbrowser.provider: In Vitest 4, import the provider factory from the provider package, such asplaywright()restoreMocks/unstubEnvs: Prevent test pollution across filessetupFiles: Run shared test initialization such as MSW, globals, or polyfills before test files
Project Structure
my-app/
├── src/
│ ├── feature.ts
│ ├── feature.test.ts
│ └── feature.browser.test.ts
├── vitest.config.ts
├── vite.config.ts
└── package.jsonWhy this matters: Keeping Node and Browser Mode tests clearly separated makes provider setup, test selection, and troubleshooting much simpler.
Choose the right environment: Prefer jsdom for most component tests and lightweight DOM assertions. Use Browser Mode when native browser APIs, real layout/event behavior, or screenshot assertions matter.
Choose the right execution model: Prefer forks for stability and native-module compatibility, especially in mixed or infrastructure-heavy suites. Reach for threads only when you know the test environment is safe for worker-thread execution and the extra speed matters.
Common Patterns
Type-safe module mock pattern
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { getUserName } from './get-user-name';
import * as api from './api';
vi.mock(import('./api'), () => ({
fetchUser: vi.fn(),
}));
describe('getUserName', () => {
beforeEach(() => {
vi.mocked(api.fetchUser).mockReset();
});
it('returns the fetched user name', async () => {
vi.mocked(api.fetchUser).mockResolvedValue({ id: '1', name: 'Ada' });
await expect(getUserName('1')).resolves.toBe('Ada');
});
});Browser Mode interaction pattern
import { expect, test } from 'vitest';
import { page, userEvent } from 'vitest/browser';
import { render } from 'vitest-browser-react';
import { Counter } from './counter';
test('increments after click', async () => {
render(<Counter />);
await userEvent.click(page.getByRole('button', { name: /increment/i }));
await expect.element(page.getByText('1')).toBeInTheDocument();
});In-source testing pattern
export function sum(a: number, b: number) {
return a + b;
}
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it('adds numbers', () => {
expect(sum(1, 2)).toBe(3);
});
}Dependencies
Required
| Package | Version | Purpose |
|---|---|---|
vitest | ^4 | Test runner and assertion/mocking APIs |
vite | ^6 | Shared Vite-powered module pipeline |
node | >=20 | Required runtime for Vitest 4 |
Optional
| Package | Version | Purpose |
|---|---|---|
@vitest/coverage-v8 | ^4 | Fast, accurate coverage with AST remapping |
@vitest/coverage-istanbul | ^4 | Istanbul coverage backend |
@vitest/browser-playwright | ^4 | Playwright provider for Browser Mode |
@vitest/browser-webdriverio | ^4 | WebdriverIO provider for Browser Mode |
@vitest/browser-preview | ^4 | Preview provider for Browser Mode |
Official Documentation
- Vitest v4 Docs
- LLM index
- Mocking Guide
- Browser Mode Guide
- Coverage Guide
- Projects Guide
- Writing Tests with AI
Troubleshooting
Agent run hangs forever
Symptoms: The test process never exits or Claude waits for additional file changes.
Solution:
vitest runBrowser Mode test cannot spy on export
Symptoms: vi.spyOn() throws, does nothing, or works in Node mode but fails in browser.
Solution:
vi.mock(import('./module'), { spy: true });Coverage misses source files with no tests
Symptoms: The report only contains files touched by executed tests.
Solution:
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
}Legacy worker or pool settings break after upgrade
Symptoms: Old maxThreads, maxForks, singleThread, singleFork, or poolOptions settings stop working after moving to Vitest 4.
Solution:
test: {
maxWorkers: 4,
}Why: Vitest 4 simplified worker configuration and removed several older pool-specific options.
Setup Checklist
Before using this skill, verify:
- [ ] Node.js is
>=20 - [ ]
viteis>=6 - [ ]
vitest.config.tsorvite.config.tscontains atestblock - [ ] Agent/CI commands use
vitest run - [ ] Coverage provider packages are installed if coverage is enabled
- [ ] Browser provider packages are installed if Browser Mode is enabled
Vitest 4 Testing Skill
Write, configure, and debug Vitest 4 test suites with Vite-native patterns.
| Status | Active |
| Version | 1.0.0 |
| Last Updated | 2026-04-12 |
| Confidence | 4/5 |
| Production Tested | https://v4.vitest.dev/ |
What This Skill Does
Provides expert assistance for Vitest 4 projects, from initial vitest.config.ts setup through unit, integration, and browser-based test authoring. It focuses on agent-safe execution, type-safe mocking with vi, Browser Mode providers, coverage configuration, setup files, and the Vitest 4 migration details that commonly break older test setups.
Core Capabilities
- Configure
vitest.config.tsfor Node, jsdom, and Browser Mode test suites - Write Vitest-native tests with
test,expect, andviAPIs - Implement module, function, global, and environment mocks without cross-test leakage
- Configure V8 or Istanbul coverage with Vitest 4-compatible settings
- Set up Browser Mode using Playwright, WebdriverIO, or Preview providers with Vitest 4 provider-factory config
- Migrate deprecated workspace-style configs to
projects - Tune execution pools, isolation, and cache behavior for correctness vs speed
- Improve AI-generated tests by grounding them in real implementation and config context
Auto-Trigger Keywords
Primary Keywords
Exact terms that strongly trigger this skill:
- vitest
- vitest 4
- vitest.config.ts
- vi.mock
- vi.fn
- vi.spyOn
- vitest/browser
- browser mode
Secondary Keywords
Related terms that may trigger in combination:
- projects config
- v8 coverage
- toMatchScreenshot
- import.meta.vitest
- jsdom test
- test.each
- mocked module
- jest migration
Error-Based Keywords
Common error messages that should trigger this skill:
- "ReferenceError: jest is not defined"
- "Cannot spy on export"
- "Coverage option \"all\" is not supported"
- "Tests are hanging in watch mode"
- "workspace config is deprecated"
- "Browser provider is missing"
- "Unknown browser provider"
Known Issues Prevention
| Issue | Root Cause | Solution |
|---|---|---|
| Tests hang forever | Watch mode used in non-interactive environment | Use vitest run |
| Browser spy fails | vi.spyOn used on sealed ESM namespace in browser | Use vi.mock(import('./mod'), { spy: true }) |
| Coverage misses untested files | coverage.include omitted | Add explicit source globs |
| Mocks leak across tests | Cleanup flags not enabled | Set restoreMocks / clearMocks / unstubEnvs |
| Multi-project setup breaks | Old workspace terminology/config copied forward | Use projects + defineProject |
| Browser provider config errors | Vitest 3 string provider examples copied into Vitest 4 | Import provider factories like playwright() from the provider package |
When to Use
Use This Skill For
- Creating or fixing
vitest.config.ts - Writing unit, integration, or browser tests in Vitest 4
- Mocking modules, constructors, globals, env vars, and timers with
vi - Setting up coverage reporting in CI or local runs
- Migrating from Jest or older Vitest configs
- Making AI-generated tests more reliable and less hallucination-prone
Don't Use This Skill For
- Jest-only codebases that are not using Vitest
- End-to-end browser automation outside Vitest Browser Mode
- Test runners that do not share Vite/Vitest configuration
Version Policy
[!NOTE]
This skill targets Vitest 4+. It assumes Node.js 20+ and Vite 6+. Browser Mode examples use the Vitest 4 provider-factory API such as playwright(). When exact version timing matters, verify feature availability in the official Vitest changelog and provider package docs.Quick Usage
# Install Vitest 4
npm install -D vitest vite
# Run once (agent/CI safe)
npx vitest run
# Run coverage
npx vitest run --coverage
# Initialize Browser Mode setup
npx vitest init browser
# Run a single file
npx vitest run src/example.test.tsToken Efficiency
| Approach | Estimated Tokens | Time |
|---|---|---|
| Manual Vitest debugging | ~12,000 | 60-90 min |
| With This Skill | ~6,000 | 20-30 min |
| Savings | 50% | ~40 min |
Reference Documentation
For deeper guidance on the most failure-prone areas, see:
| Topic | Reference File | Purpose |
|---|---|---|
| Mocking | `mocking-reference.md` | Module mocks, hoisting rules, env/global stubs, and safe cleanup |
| Browser Mode | `browser-mode-reference.md` | Providers, vitest/browser patterns, native event caveats, and spying pitfalls |
| Coverage & Projects | `coverage-projects-reference.md` | Vitest 4 coverage changes, projects, and migration-safe config patterns |
| Pools & Execution | `pools-execution-reference.md` | forks vs threads, isolation tradeoffs, persistent cache, and large-suite performance tuning |
See the References Index for navigation.
Environment Heuristic
- Use `jsdom` for most component tests, DOM assertions, and fast feedback loops.
- Use Browser Mode when native browser APIs, real event behavior, layout, or screenshot assertions are required.
File Structure
vitest-v4/
├── SKILL.md # Quick-start patterns, critical rules, and practical guidance
├── README.md # This file - discovery and quick reference
└── references/
├── README.md # Reference index
├── mocking-reference.md # Mocking APIs, hoisting, and cleanup
├── browser-mode-reference.md # Browser Mode provider and test patterns
├── coverage-projects-reference.md # Coverage/provider/projects guidance
└── pools-execution-reference.md # Execution pools, isolation, and performance tuningDependencies
| Package | Version | Verified |
|---|---|---|
vitest | ^4 | 2026-04-12 |
vite | ^6 | 2026-04-12 |
node | >=20 | 2026-04-12 |
Preview provider caveat: @vitest/browser-preview can help with local debugging, but it is not a strong CI default because it does not provide the same browser fidelity as Playwright or WebdriverIO.Official Documentation
- Vitest v4 Docs
- LLM Index
- Mocking Guide
- Browser Mode Guide
- Coverage Guide
- Projects Guide
- Writing Tests with AI
Related Skills
github-actions- CI workflow patterns when running Vitest in GitHub Actionsvite-v8- Vite config guidance when Vitest shares or extends build configurationreact-performance- Useful when Vitest covers React render and interaction regressions
---
License: MIT
Vitest 4 Browser Mode Reference
Browser Mode is stable in Vitest 4 and is the right choice when DOM behavior, native browser events, or screenshot assertions matter.
Provider packages
Install one of:
@vitest/browser-playwright@vitest/browser-webdriverio@vitest/browser-preview
@vitest/browser-preview is best treated as a local preview/debugging option rather than a CI-grade provider.
Basic config
import { defineConfig } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
export default defineConfig({
test: {
browser: {
enabled: true,
provider: playwright(),
instances: [{ browser: 'chromium' }],
},
},
});In Vitest 4, Browser Mode providers use factory imports from the provider packages. Older string-style provider values belong to pre-v4 examples.
Interaction helpers
Use helpers from vitest/browser, not generic synthetic helpers when native browser realism matters.
import { page, userEvent } from 'vitest/browser';This matters because Vitest Browser Mode is designed to work with the real browser runtime rather than a jsdom approximation.
When in doubt, prefer userEvent from vitest/browser over @testing-library/user-event because Vitest's browser helpers are designed to route through the real browser provider instead of simulating everything in-process.
Screenshot testing
Vitest 4 includes built-in screenshot assertions like toMatchScreenshot(). Keep screenshot inputs deterministic and avoid random data or clock-dependent rendering.
Also separate screenshot suites from general interaction suites when provider startup cost or rendering determinism becomes a problem.
Blocking dialogs
Avoid unmocked alert, confirm, and prompt. These can block browser communication and hang the run.
Spying caveat
Native ESM namespace objects are sealed in the browser. Use vi.mock(import('./module'), { spy: true }) instead of vi.spyOn() on module exports.
Locator and assertion pattern
Prefer provider-native locator flows:
await expect.element(page.getByRole('button', { name: /save/i })).toBeVisible();Use the expect.element(...) wrapper for browser-element assertions rather than assuming standard Node-side matcher semantics.
Vitest 4 Coverage and Projects Reference
Vitest 4 tightened a few configuration details that commonly break older examples.
Coverage providers
Choose one:
@vitest/coverage-v8- usually the fastest option@vitest/coverage-istanbul- useful when Istanbul-specific behavior is needed
Vitest 4 improved V8 coverage accuracy with AST-based remapping, so V8 is now a strong default for most projects.
Important coverage change
coverage.all and coverage.extensions are removed in Vitest 4.
Vitest 4 also changed V8 coverage remapping to an AST-based approach, so coverage percentages can legitimately shift after upgrade even when your source code did not change.
Use coverage.include instead:
coverage: {
provider: 'v8',
include: ['src/**/*.{ts,tsx}'],
}Keep include focused on real source files. Do not point it at node_modules, build output, or broad repo-root globs.
Projects replace workspace-style config
Vitest 4 uses projects as the stable term for multi-project setups. The rename started in Vitest 3.2, and older workspace-file usage is removed in Vitest 4.
The key gotcha is inheritance: treat project configs as explicit units. Shared concerns like reporters, coverage, or common setup should be intentionally factored into a base config via extends: true or mergeConfig, not assumed to flow in automatically.
import { defineConfig, defineProject } from 'vitest/config';
import { playwright } from '@vitest/browser-playwright';
export default defineConfig({
test: {
projects: [
defineProject({
test: {
name: 'unit',
include: ['src/**/*.test.ts'],
},
}),
defineProject({
test: {
name: 'browser',
include: ['src/**/*.browser.test.ts'],
browser: {
enabled: true,
provider: playwright(),
instances: [{ browser: 'chromium' }],
},
},
}),
],
maxWorkers: 4,
},
});Use projects when environments differ meaningfully, like Node vs Browser Mode, or app vs package-level tests in a monorepo.
Worker migration note
If an older config uses maxThreads, maxForks, singleThread, singleFork, or poolOptions, migrate those settings to Vitest 4 worker options such as maxWorkers.
AI-output optimization note
Vitest 4 includes agent-aware output behavior in some reporting flows. When optimizing for AI or CI summaries, prefer compact summary output over huge raw reports unless a human is actively diagnosing coverage deltas.
Reference pattern for shared base config
import { defineConfig, mergeConfig } from 'vitest/config';
const shared = defineConfig({
test: {
restoreMocks: true,
coverage: {
provider: 'v8',
},
},
});Vitest 4 Mocking Reference
Vitest 4 uses the vi utility for mocks, spies, stubs, and timers. Prefer Vitest-native patterns over Jest-compatible muscle memory.
Core APIs
vi.fn()- create a mock functionvi.spyOn(obj, 'method')- spy on a property or method in Node/jsdom modevi.mock(import('./module'), factory)- mock a module with type-safe importsvi.stubEnv('NAME', 'value')- stub env valuesvi.stubGlobal('fetch', mock)- stub globals
Architecture Note
Vitest 4 runs on Vite's native Module Runner rather than the older vite-node executor model. That matters because seemingly odd mocking or path-resolution behavior is often runner-related, not just test-code related.
Type-safe module mock pattern
import { vi } from 'vitest';
import * as api from './api';
vi.mock(import('./api'), () => ({
fetchUser: vi.fn(),
}));
vi.mocked(api.fetchUser).mockResolvedValue({ id: '1', name: 'Ada' });Hoisting rule
vi.mock() is hoisted. Treat it like a file-level declaration, not normal runtime code.
If local variables are needed before the mock factory runs, reach for vi.hoisted() instead of trying to outsmart hoisting with ordinary top-level state.
const { mockToken } = vi.hoisted(() => ({
mockToken: 'test-token',
}));Bad pattern
const token = makeToken();
vi.mock(import('./auth'), () => ({
getToken: () => token,
}));Better pattern
const mockedGetToken = vi.fn();
vi.mock(import('./auth'), () => ({
getToken: mockedGetToken,
}));Cleanup strategy
Prefer config-driven cleanup whenever possible:
test: {
restoreMocks: true,
clearMocks: true,
unstubEnvs: true,
unstubGlobals: true,
}Use per-test cleanup only when a project intentionally leaves some state intact.
Importing the real implementation
Use vi.importActual() when you need a partial mock that preserves most of the original module:
vi.mock(import('./math'), async () => {
const actual = await vi.importActual<typeof import('./math')>('./math');
return {
...actual,
add: vi.fn(() => 10),
};
});Non-hoisted mocking
Use vi.doMock() when the mock must be created later at runtime rather than hoisted at module load time.
Constructor behavior
Vitest 4 improved constructor-aware spying and mocking. This matters in class-heavy code because new calls now behave more predictably under vi.spyOn() / vi.fn() than many older examples suggest.
Module directories
When debugging custom module resolution behavior, know that older VITE_NODE_DEPS_MODULE_DIRECTORIES references have moved to VITEST_MODULE_DIRECTORIES in the new architecture.
Timers
For timer-driven code, prefer explicit fake timer control:
vi.useFakeTimers();
vi.advanceTimersByTime(1000);
vi.useRealTimers();Constructors in Vitest 4
Vitest 4 supports constructor-aware vi.fn() and vi.spyOn() behavior, which makes class-heavy mocks less awkward than older releases.
Browser Mode caveat
Do not rely on vi.spyOn(moduleNamespace, 'exportedFn') in Browser Mode. Browser ESM namespace objects are sealed.
Use:
vi.mock(import('./module'), { spy: true });instead of:
vi.spyOn(moduleNamespace, 'exportedFn');Vitest v4 Pools and Execution Reference
Large or flaky suites often need execution tuning as much as they need better assertions.
Pool Selection Heuristic
| Pool | Best For | Main Risk |
|---|---|---|
forks | Stability, native modules, mixed infra tests | Slightly slower than threads |
threads | Fast pure-JS suites with safe worker-thread behavior | Native module and runtime compatibility issues |
vmThreads / vmForks | Sandbox-style experiments only | Memory leaks and global-object mismatch surprises |
Recommended Default
Start with forks unless you have a measured reason not to. It is the safer default for real-world suites that touch native dependencies, runtime integration layers, or tooling that assumes process isolation.
Isolation Tradeoff
isolate: false can speed up pure Node suites significantly, but only use it when tests are intentionally side-effect-safe and you understand the shared-state risks.
Persistent Module Cache
For large codebases, experimental.fsModuleCache can cut cold-start cost by persisting transformed modules between runs.
Debugging Heuristic
- Native dependency failures in
threads→ tryforks - Memory weirdness in VM pools → remove VM pools first
- Slow cold starts in huge repos → evaluate filesystem module cache
- Random state leakage → review isolation mode before rewriting tests
Vitest 4 Skill References
Use these references when the main SKILL.md is not enough:
| File | Focus |
|---|---|
| `mocking-reference.md` | vi.mock, vi.fn, vi.spyOn, env/global stubs, hoisting, and cleanup rules |
| `browser-mode-reference.md` | Provider setup, vitest/browser, event realism, screenshots, and Browser Mode caveats |
| `coverage-projects-reference.md` | V8/Istanbul coverage setup, coverage.include, and multi-project config |
| `pools-execution-reference.md` | forks vs threads, isolation tradeoffs, fs module cache, and large-suite execution tuning |
Suggested Reading Order
- Writing or fixing mocks? Start with
mocking-reference.md - Running tests in a real browser? Start with
browser-mode-reference.md - Fixing coverage or multi-project config? Start with
coverage-projects-reference.md - Tuning speed or debugging runner instability? Start with
pools-execution-reference.md