Vitest
Vitest provides fast TypeScript test runs, watch mode, mocking, and coverage for Node backends that outgrow bare node:test.
Recipe
Quick-reference recipe card - copy-paste ready.
npm install -D vitest @vitest/coverage-v8// vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
environment: "node",
include: ["test/**/*.test.ts", "src/**/*.test.ts"],
coverage: { provider: "v8", reporter: ["text", "lcov"] },
},
});{ "scripts": { "test": "vitest run", "test:watch": "vitest" } }When to reach for this:
- Heavy
vi.mock()usage for external APIs and databases. - Watch mode during TDD on TypeScript services.
- Coverage thresholds in CI.
Working Example
// src/users/client.ts
export async function fetchUser(id: string): Promise<{ id: string; name: string }> {
const res = await fetch(`https://api.example.com/users/${id}`);
if (!res.ok) throw new Error("upstream error");
return res.json() as Promise<{ id: string; name: string }>;
}// test/users/client.test.ts
import { afterEach, describe, expect, it, vi } from "vitest";
import { fetchUser } from "../../src/users/client.js";
afterEach(() => vi.restoreAllMocks());
describe("fetchUser", () => {
it("returns user json", async () => {
vi.stubGlobal(
"fetch",
vi.fn().mockResolvedValue({
ok: true,
json: async () => ({ id: "1", name: "Ada" }),
}),
);
await expect(fetchUser("1")).resolves.toEqual({ id: "1", name: "Ada" });
});
});# CI
- run: npm run test -- --coverageWhat this demonstrates:
vi.stubGlobalreplacesfetchwithout dependency injection refactor.vitest runsingle pass for CI;vitestwatch locally.- V8 coverage integrates with Codecov/Sonar.
Deep Dive
How It Works
- Vitest uses Vite's transform pipeline for fast TS transpile (esbuild/swc).
- Compatible with Jest-like API (
describe,it,expect,vi). environment: "node"avoids jsdom unless testing DOM.- Pool threads run test files in parallel.
Config Tips
| Option | Purpose |
|---|---|
setupFiles | Global test setup (env vars) |
testTimeout | Raise for integration tests |
pool: "forks" | Isolate native addon crashes |
TypeScript Notes
vitest/configtypesdefineConfig.- Path aliases from
tsconfigcan be wired viavite-tsconfig-pathsplugin if needed.
Gotchas
- Mixing Vitest and node:test - Two runners confuse contributors. Fix: pick one per repo or per package in monorepo.
- Mocking ESM modules - Hoisting issues with
vi.mock. Fix: usevi.mockfactory and dynamic imports after mock. - Integration tests in default pool - DB race conditions. Fix: separate
vitest.integration.config.tswithfileParallelism: false. - Coverage on branch-only code - False confidence. Fix: pair coverage with mutation testing or behavior assertions sparingly.
- Snapshot churn - Large PR noise. Fix: prefer explicit
toEqualon stable fields.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
| node:test | Zero deps, simple unit tests | Heavy mocking needs |
| Jest | Existing brownfield suites | Greenfield preferring speed |
| tap | TAP consumers | Team wants Vitest DX |
FAQs
Is Vitest only for Vite frontends?
No. environment: "node" targets backend APIs and workers.
How do I mock a module?
vi.mock("../src/db.js", () => ({ query: vi.fn() }));Can Vitest test Express apps?
Yes with Supertest or app.inject for Fastify. Export createApp() without listening.
How do I set coverage thresholds?
coverage: { thresholds: { lines: 80, functions: 80 } }Does it work in monorepos?
Per-package vitest.config.ts or root config with projects array.
How do I test NestJS services?
Use @nestjs/testing Test.createTestingModule inside Vitest describe blocks.
Watch mode in Docker?
Poor DX; use watch locally, vitest run in containers.
ESM or CJS?
Prefer "type": "module" projects; Vitest handles ESM natively on Node 24.
How do I debug a test?
vitest --inspect-brk or VS Code Vitest extension.
Parallel vs serial?
describe.sequential or separate config for DB integration suites.
Related
- node:test & node:assert - zero-dep alternative
- Supertest & HTTP Integration - HTTP testing
- Testing Best Practices - behavior over implementation
Stack versions: This page was written for Node.js 24.18.0 (Active LTS), npm 10+, TypeScript 5.6+, Express 5, Fastify 5, and NestJS 11.