Skip to content

ADR-0004: Vitest as test runner

Status: Accepted Date: 2026-04-20

We need a test runner that works across a TypeScript monorepo, handles ESM cleanly, starts fast enough for tight inner-loop iteration, and extends to Cloudflare Workers when we eventually test Worker-bound code.

Use Vitest 3 as the test runner, configured at the repository root (vitest.config.ts). Tests colocate with source as *.test.ts.

  • Native ESM and TypeScript. No Babel/ts-jest transform pipeline to maintain.
  • Familiar API surface (describe, it, expect) — trivial for anyone coming from Jest.
  • @cloudflare/vitest-pool-workers exists for the day we write Worker-targeted tests; same runner, different environment.
  • Watch mode and HMR-style test reruns are extremely fast.
  • Vitest moves quickly. Breaking changes between minors are more common than with Jest. Mitigated by pinning the exact version.
  • --passWithNoTests is needed on a fresh scaffold to avoid exit-1 on empty test-globs. Enabled by default in our scripts.
  • Jest: slower on TS/ESM, larger config surface, transform pipeline overhead. The ecosystem is increasingly migrating away.
  • Node built-in test runner (node --test): minimal, standard-library-only, no mocking/spying primitives, no watch mode worth using. Fine for very small libraries; not enough for us.
  • Bun test: fast, clean API, but ties us to Bun as a runtime for development. Rejected alongside Bun workspaces (ADR-0001).
  • Playwright for everything: rejected — overkill and slow for non-browser unit tests. We’ll reach for Playwright when we have a UI to test.