Test Runners — Vitest & Unit Testing

Test Runners — Vitest & Unit Testing: Vitest is like a teacher checking every assignment in the class at once, but only re-reading the pages that actually changed — sheets unchan

Vitest is like a teacher checking every assignment in the class at once, but only re-reading the pages that actually changed — sheets unchanged since the last check aren't read from scratch again; only the touched files get rescanned (watch mode), and results show up in seconds as ✅/❌. So if Jest is already an established test runner, why did a new tool like Vitest need to exist? Because Jest runs TypeScript files through a separate compile step first (usually Babel or ts-jest), and that creates a noticeable slowdown on large projects; Vitest instead reuses the esbuild-based instant compilation that Vite already runs internally, shrinking that step to nearly zero — the closest Java analogy is Maven doing incremental compilation instead of rebuilding the entire project every time. The concrete QA payoff: in a TypeScript project with hundreds of test files, changing just one file makes Vitest's watch mode re-run only the relevant tests within seconds; the same scenario under Jest can feel noticeably slower because of that compile step.

Vitest: The Vite-Native Test Runner

Playwright Test already ships its own test runner for end-to-end browser tests — you don't need Vitest for that. But the moment you write a plain TypeScript helper function (a price formatter, a data parser, a custom wait utility), you want to unit test that function in isolation, without spinning up a browser. That's where Vitest comes in: a fast, Jest-compatible test runner built on top of Vite, with zero-config TypeScript support. Jest is the older, more established alternative — same API shape (describe, it, expect), slower startup, and requires ts-jest to understand TypeScript out of the box.

Unit test flow with Vitest

Vite-based ultra-fast test runner — describe/it/expect API identical to Jest

describe for group, it for single test, expect for assertion — use all three layers

npx vitest run runs once for CI; npx vitest watch is for development mode

Order the steps of testing a function with Vitest:

Import the function to test

Define test group with describe

Write scenario with it and verify with expect

Run tests with npx vitest run

it('errors on negative input', () => { expect(() => formatPrice(-50)).toThrow('...') }) — why does expect wrap a function?

To catch code that throws — direct call would crash the test