The Test Runner Most Modern Node Projects Use
Vitest
Vitest is fast, has a great UI, mocks, snapshots, and a Jest-compatible API. The default for new projects.
What you'll learn
- Install and run Vitest
- Write tests with describe/it/expect
- Use mocks and spies
Vitest is the test runner most new Node/Vite projects pick. Fast, TypeScript-friendly, Jest-compatible API.
Install
npm install --save-dev vitest A First Test
// math.test.js
import { test, expect } from "vitest";
import { add } from "./math.js";
test("add: sums two numbers", () => {
expect(add(2, 3)).toBe(5);
});
test("add: order doesn't matter", () => {
expect(add(2, 3)).toBe(add(3, 2));
}); Run
npx vitest # watch mode by default
npx vitest run # single pass (for CI) describe, it, expect
import { describe, it, expect, beforeEach } from "vitest";
describe("Cart", () => {
let cart;
beforeEach(() => { cart = new Cart(); });
it("starts empty", () => {
expect(cart.items).toHaveLength(0);
});
it("adds items", () => {
cart.add({ id: 1, price: 10 });
expect(cart.total).toBe(10);
});
}); expect has dozens of matchers: .toBe, .toEqual, .toContain,
.toThrow, .toBeCloseTo, .toMatchObject, .toMatchSnapshot.
Mocks
import { vi, expect, test } from "vitest";
import { sendEmail } from "./email.js";
vi.mock("./email.js", () => ({
sendEmail: vi.fn().mockResolvedValue({ id: "msg_123" }),
}));
test("signup sends a welcome email", async () => {
await signup({ email: "ada@example.com" });
expect(sendEmail).toHaveBeenCalledWith("ada@example.com", /welcome/i);
}); Coverage
npx vitest run --coverage Outputs an HTML coverage report — great for finding untested branches.
UI
npx vitest --ui A browser UI showing test status, output, and a file tree. Surprisingly useful for big test suites.
Why It Won
- Same API as Jest (easy migration)
- Native ESM + TypeScript out of the box
- Vite-powered (super fast HMR for tests)
- Built-in coverage, mocks, snapshots, UI
- Works for backend AND frontend (JSX, jsdom)
For Node-only libraries: node:test is fine. For everything else:
Vitest.