codingstairs
NotesEDULifeContact
⌕Search⌘K
koen

Navigation

  • Intro
  • Blog
  • Life

Get in touch

Send without signing in. Add your email if you'd like a reply.

  • Leave a message anonymously →
  • ✉ warragon112@gmail.com
  • KakaoTalk Open Chat ↗

© 2026 codingstairs

  • Notes
  • EDU
  • Search
  • Life
  • Contact
  • Legal
  • RSS
  • GitHub
EDU›Testing strategy and quality gates›Step 2

Step 2

vitest basics + mock patterns

0 views

vitest basics + mock patterns

The Jest successor. ESM-first, Vite-native, fast.

1. Install

pnpm add -D vitest @vitest/ui
{ "scripts": { "test": "vitest run", "test:watch": "vitest" } }

2. First test

// src/lib/format.test.ts
import { describe, it, expect } from "vitest";
import { formatDate } from "./format";

describe("formatDate", () => {
  it("YYYY-MM-DD", () => {
    expect(formatDate(new Date("2026-05-06"))).toBe("2026-05-06");
  });
});

3. Environment — node vs jsdom

export default defineConfig({
  test: { environment: "node", globals: true },
});

Use jsdom for DOM-dependent code. Per-file override:

// @vitest-environment jsdom

4. vi.hoisted

const { mockQuery } = vi.hoisted(() => ({ mockQuery: vi.fn() }));
vi.mock("@/shared/lib/db", () => ({ pool: { query: mockQuery } }));

import { getUserById } from "./user-repo";

it("returns row", async () => {
  mockQuery.mockResolvedValueOnce({ rows: [{ id: 1 }] });
  expect(await getUserById(1)).toEqual({ id: 1 });
});

vi.mock hoists but forbids outer references. Hoist the mock construction with vi.hoisted.

5. env · global stubs

beforeEach(() => {
  vi.stubEnv("REVALIDATE_URL", "http://localhost:3000");
  vi.resetModules();
});
afterEach(() => { vi.unstubAllEnvs(); vi.unstubAllGlobals(); });

it("fetch called", async () => {
  const fetchMock = vi.fn().mockResolvedValue({ ok: true, status: 200 });
  vi.stubGlobal("fetch", fetchMock);
  await triggerRevalidate();
  expect(fetchMock).toHaveBeenCalled();
});

6. React components

pnpm add -D @testing-library/react @testing-library/jest-dom
// @vitest-environment jsdom
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";

it("increments on click", async () => {
  render(<Counter />);
  await userEvent.click(screen.getByRole("button", { name: /increment/i }));
  expect(screen.getByText("1")).toBeInTheDocument();
});

7. snapshots — carefully

expect(user).toMatchSnapshot();

Convenient but snapshots rot. Only for complex outputs.

8. Coverage

pnpm add -D @vitest/coverage-v8
vitest run --coverage
test: {
  coverage: {
    provider: "v8",
    exclude: ["**/*.test.ts", "**/dist/**"],
    thresholds: { lines: 70, functions: 70, branches: 70 },
  },
}

9. Gotchas

  • "Cannot access before initialization" on vi.mock vars → use vi.hoisted
  • Missing afterEach cleanup → env stubs leak into later tests
  • No window in tests → set environment: "jsdom"
  • Stale snapshots → review periodic regenerations

Closing

Nail vi.hoisted, stubEnv, and stubGlobal and you can handle virtually any mocking situation.

Next

  • 03-pytest-fixtures

← Step 1

Test pyramid · trade-offs

Step 3 →

pytest · fixtures · parametrize