vitest
Use when writing unit and integration tests with Vitest for TypeScript or JavaScript projects built with Vite — test layout, deterministic tests for seeded simulations, threshold tests that turn bench
Files1
View on GitHubSKILL.mdskills/tech/vitest/SKILL.md
GitHubVitest
Default: vitest run in CI and vitest (watch) locally, tests next to code (*.test.ts) or in tests/, environment: "node" unless DOM is needed, no network in unit tests.
Setup
// vitest.config.ts (or the `test` key in vite.config.ts)
import { defineConfig } from "vitest/config";
export default defineConfig({
test: { environment: "node", include: ["src/**/*.test.ts", "tests/**/*.test.ts"], testTimeout: 10_000 },
});
Use environment: "jsdom" (or happy-dom) per file with a // @vitest-environment jsdom comment, so most tests stay fast in Node.
Tests that carry weight
- Determinism: same seed → same result.
expect(run(seed)).toEqual(run(seed))and a snapshot of a short summary (not of huge objects). - Thresholds from benchmarks: run an automated player over several seeds and assert ranges, e.g. "first enemy defeated within 15 s", "no deaths in the first zone", "win rate of the final boss between 55 % and 85 %". This turns "feel" into regressions that fail.
- Invariants over examples: "every level is solvable", "all locales have all keys", "no two items overlap", "every UI action has a sound and an effect".
- Derived numbers: assert that UI text uses counts derived from data, so adding content does not break tests.
Mocking
vi.useFakeTimers()+vi.advanceTimersByTime(ms)for time-based logic; restore withvi.useRealTimers().vi.mock("./module")for module boundaries; prefer dependency injection for anything you mock often.vi.spyOn(console, "error")and fail tests that log unexpected errors.
Keeping it fast
- Separate heavy suites (
test:full) from the PR gate (test), and run the heavy one locally before releases. - Avoid real
setTimeoutwaits; use fake timers or direct calls. - Keep fixtures small; generate large inputs in code from a seed.
Verification loop
npx vitest runpasses with no skipped tests that should run.npx vitest run --coverageon core logic when coverage is a goal (do not chase 100 % on rendering code).- Run twice in a row; a test that fails on the second run shares state.
Pitfalls
- Shared module state between tests (singletons, caches) causes order-dependent failures; reset in
beforeEachor isolate. - Floating point: use
toBeCloseTofor computed values. - Snapshot drift: reviewers approve large snapshot diffs without reading; keep snapshots small and meaningful.
- Hidden network calls (fetching data in a module's top level) make tests slow and flaky; move I/O behind functions.
- Timezone-dependent dates: set
TZin the test script (TZ=UTC vitest run) or inject the clock.