Vitest
Vitest provides fast TypeScript test runs, watch mode, mocking, and coverage for Node backends that outgrow bare node:test.
Search across all documentation pages
Vitest provides fast TypeScript test runs, watch mode, mocking, and coverage for Node backends that outgrow bare node:test.
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:
vi.mock() usage for external APIs and databases.// 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.stubGlobal replaces fetch without dependency injection refactor.vitest run single pass for CI; vitest watch locally.describe, it, expect, vi).environment: "node" avoids jsdom unless testing DOM.| Option | Purpose |
|---|---|
setupFiles | Global test setup (env vars) |
testTimeout | Raise for integration tests |
pool: "forks" | Isolate native addon crashes |
vitest/config types defineConfig.tsconfig can be wired via vite-tsconfig-paths plugin if needed.vi.mock. Fix: use vi.mock factory and dynamic imports after mock.vitest.integration.config.ts with fileParallelism: false.toEqual on stable fields.| 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 |
No. environment: "node" targets backend APIs and workers.
vi.mock("../src/db.js", () => ({ query: vi.fn() }));Yes with Supertest or app.inject for Fastify. Export createApp() without listening.
coverage: { thresholds: { lines: 80, functions: 80 } }Per-package vitest.config.ts or root config with projects array.
Use @nestjs/testing Test.createTestingModule inside Vitest describe blocks.
Poor DX; use watch locally, vitest run in containers.
Prefer "type": "module" projects; Vitest handles ESM natively on Node 24.
vitest --inspect-brk or VS Code Vitest extension.
describe.sequential or separate config for DB integration suites.
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.
Reviewed by Chris St. John·Last updated Jul 18, 2026