Testing#

The 2026 default in JavaScript is vitest, Jest-compatible API on top of Vite, fast watch mode and ESM-native. jest is still the right choice on projects that already standardised on it; mocha plus chai is the older xUnit-flavoured alternative; Node’s own node --test runner (node:test) ships with the runtime.

For e2e and browser tests, see playwright. For HTTP mocking, see msw and nock.

vitest#

$ npm install --save-dev vitest

Specs live alongside source files (foo.test.js) or under test/. vitest discovers them automatically.

import {describe, it, expect} from "vitest";
import {trim} from "./string-helpers.js";

describe("trim", () => {
  it("removes trailing whitespace", () => {
    expect(trim("hi   ")).toBe("hi");
  });

  it("leaves clean strings alone", () => {
    expect(trim("hi")).toBe("hi");
  });

  describe("when given null", () => {
    it("throws", () => {
      expect(() => trim(null)).toThrow(TypeError);
    });
  });
});
$ npx vitest
✓ trim (3)
   removes trailing whitespace
   leaves clean strings alone
   when given null > throws

Setup and teardown#

beforeEach / afterEach run around every it; beforeAll / afterAll run once per describe.

describe("Server", () => {
  let server;

  beforeEach(async () => {
    server = await Server.start(8080);
  });

  afterEach(async () => {
    await server.stop();
  });

  it("answers ping", async () => {
    expect(await server.ping()).toBe("pong");
  });
});

Expectations#

The fluent expect(value).toX(...) API. Most-used matchers.

Matcher

Meaning

.toBe(x)

strict equality (===)

.toEqual(x)

deep structural equality

.toStrictEqual(x)

deep + type check (no missing keys)

.toBeTruthy() / .toBeFalsy()

boolean coercion

.toBeNull() / .toBeUndefined()

.toContain(x)

array / string membership

.toHaveLength(n)

.toMatch(re)

string matches a regex

.toThrow(msg or Class)

calling a function throws

.resolves.toBe(x) / .rejects.toThrow()

async assertions

await expect(fetchUser(1)).resolves.toMatchObject({id: 1});
await expect(fetchUser(0)).rejects.toThrow("invalid id");

Mocks and spies#

vi.fn() makes a spy; vi.spyOn(obj, "method") wraps an existing method; vi.mock("./module") replaces a module import.

import {vi} from "vitest";

it("logs on failure", async () => {
  const log = {warn: vi.fn()};
  await runWith(log);
  expect(log.warn).toHaveBeenCalledWith("boom");
});

vi.mock("./network.js", () => ({
  fetch: vi.fn().mockResolvedValue({ok: true}),
}));

vi.useFakeTimers() advances timers manually; vi.advanceTimersByTime(ms) runs scheduled callbacks. Useful for testing setTimeout / setInterval code without waiting.

Snapshots#

.toMatchSnapshot() writes the value to a .snap file on first run; subsequent runs compare. Update with vitest --update. Useful for stable HTML / JSON outputs.

it("renders the report", () => {
  expect(render(data)).toMatchSnapshot();
});

Coverage#

vitest runs coverage through c8 or v8 providers.

$ npm install --save-dev @vitest/coverage-v8
$ npx vitest --coverage

The report lands in coverage/ (HTML by default; JSON, lcov, text summary all configurable).

node:test (stdlib)#

Node 18+ ships its own test runner. No dependencies needed.

import {test} from "node:test";
import assert from "node:assert/strict";

test("trim removes whitespace", () => {
  assert.equal(trim("hi   "), "hi");
});
$ node --test                       # runs *.test.js
$ node --test --test-name-pattern="trim"

Lightweight; good for small libraries that do not want a test framework dependency. Lacks the matcher ergonomics of vitest.

Playwright (e2e)#

End-to-end browser tests. The operator scripts real browser behaviour (Chromium, Firefox, WebKit) and asserts on the rendered page.

$ npm install --save-dev @playwright/test
$ npx playwright install
import {test, expect} from "@playwright/test";

test("login lands on dashboard", async ({page}) => {
  await page.goto("/login");
  await page.fill('input[name="user"]', "op");
  await page.fill('input[name="pass"]', "***");
  await page.click('button[type="submit"]');
  await expect(page).toHaveURL("/dashboard");
});

References#