Jest & Vitest
JavaScript unit testing: Jest for React and Node.js, Vitest for Vite projects. Fast, modern, and developer-friendly.
Overview
Jest, created by Meta (Facebook) in 2014, is the most popular JavaScript testing framework. It provides a zero-config testing experience with built-in mocking, coverage reporting, and snapshot testing. Jest is the default choice for React applications and widely used for Node.js backend testing.
Vitest, created in 2021 by the Vite team, is a next-generation testing framework designed for Vite-based projects. It is API-compatible with Jest but significantly faster thanks to Vite's native ESM andesbuild-based bundling. Vitest is becoming the default for new Vue, Svelte, and Vite-based projects.
What it's used for
Jest/Vitest are essential for:
- JavaScript/TypeScript unit testing: The standard frameworks for testing JS/TS code.
- React component testing: Jest + React Testing Library is the industry standard.
- Node.js API testing: Test Express, Fastify, and NestJS endpoints.
- Snapshot testing: Catch unexpected UI changes in component output.
Pros & Cons
Pros
- Zero-config setup for most projects
- Built-in mocking, coverage, and snapshot testing
- Fast parallel execution
- Excellent TypeScript support
- Massive ecosystem and community
Cons
- JavaScript/TypeScript only
- Jest can be slow for very large projects (Vitest fixes this)
- ESM support in Jest has been historically problematic (improving in Jest 30)
- Snapshot testing can create large, noisy diffs
- Configuration can be complex for non-standard setups
Platforms & Integrations
Jest and Vitest run on Windows, macOS, and Linux via Node.js. They integrate with all JavaScript build tools and CI platforms.
Pricing
| Tier | Cost | Includes |
|---|---|---|
| Open Source | Free | Full framework, all features, community support |
NZ Context
Jest is ubiquitous in NZ JavaScript and React teams. Every frontend developer and full-stack JS developer in NZ knows Jest. Vitest is rapidly gaining adoption among NZ teams using Vite, particularly Vue and Svelte projects. For NZ testers working in JavaScript environments, Jest proficiency is essential; Vitest is a valuable addition for Vite-based projects.
Alternatives
- Mocha — More flexible but requires more configuration.
- AVA — Minimalist test runner with parallel execution by default.
- Node.js Test Runner — Built-in since Node.js 18. Zero dependencies for simple cases.
When to choose Jest / Vitest
A quick decision guide for NZ teams evaluating unit testing options.
| Choose Jest / Vitest when… | Choose something else when… | Combine with… |
|---|---|---|
| Your project is JavaScript or TypeScript — React, Vue, Svelte, Node.js, or a Vite-based SPA | Your stack is Python, .NET, Java, or Go — use pytest, xUnit, JUnit, or Go's built-in test package instead | MSW (Mock Service Worker) — intercepts HTTP at the network layer so components under test behave exactly as in production |
| You need fast CI feedback on a large JS codebase — Vitest's esbuild pipeline can cut Jest's build time by 40–70% on TypeScript-heavy projects | You need full browser rendering, accessibility checks, or multi-tab session testing — Jest/Vitest run in jsdom, not a real browser; use Playwright instead | React Testing Library — encourages testing what the user sees rather than implementation details; pairs with both Jest and Vitest |
| Your team is greenfield on Vite (Nuxt 3, SvelteKit, Astro, or modern React) — Vitest reuses the Vite config with zero extra transformation setup | Your project uses Create React App or Angular's default Jest config and you'd need to eject or reconfigure CI — the migration overhead outweighs the gains | Playwright — covers E2E and cross-browser journeys that Jest/Vitest cannot; the two layers are complementary, not competing |
| You are testing pure logic: date formatters, currency converters, input validators, reducers, or business-rule functions with no DOM or network dependencies | You are writing API contract tests against a live service — use Pact for consumer-driven contract testing; Jest/Vitest handle the unit layer within each service | Istanbul / v8 coverage — built into both Jest and Vitest; pipe output to Codecov or SonarQube for team-wide coverage visibility in CI |
What I would do
Practitioner judgment on tool adoption, team onboarding, and when to swap.
jest --listTests to get the full test inventory, migrate files directory by directory (starting with pure utility tests, leaving component tests for last), and keep a running count of passing tests as a migration health metric. I would not migrate everything at once; parallel running Jest and Vitest on different directories is a valid intermediate state for 1–2 weeks.jest.mock() for every dependency — including mocking React components that are simply wrappers around a heading tag — and the suite was passing but a production bug had slipped through anywayThe bottom line: A Jest/Vitest test suite that mocks everything is a liability disguised as confidence. The right signal is not how many tests pass — it is how many times your test suite has caught a real bug before a user did. If the answer is "rarely," your mock boundaries are in the wrong place.
Interview questions
Questions you are likely to get if you list Jest or Vitest on your CV — with what interviewers are really testing for.
What is the difference between a unit test and an integration test, and where do Jest and Vitest sit in that picture?
What they’re really testing: Whether you understand the purpose and boundaries of each test layer, not just that you can recite a definition.
Strong answer covers: Unit tests exercise pure logic in isolation (no network, no DOM, no shared state); integration tests wire real components together and intercept at the network layer using something like MSW; Jest/Vitest operate at both layers. Mention a concrete NZ workplace example — e.g. unit-testing a GST calculation function at a NZ payroll company vs. integration-testing the form that submits it.
When would you choose Vitest over Jest for a new project, and when would you stick with Jest?
What they’re really testing: Whether you make tool decisions based on project context rather than hype or personal preference.
Strong answer covers: Vitest is the natural choice for Vite-based stacks (SvelteKit, Nuxt 3, modern React with Vite) because it reuses the same config and transformation pipeline with no extra Babel overhead; Jest is the right call when the project is already on webpack or CRA, or when the team has deep Jest muscle memory and tooling. For NZ government or enterprise projects still on Angular or older React, switching for its own sake carries real risk with little reward.
You’re a QA engineer at a Wellington SaaS company. Your Jest suite passes on every developer’s machine but fails intermittently in GitHub Actions CI. How do you approach diagnosing this?
What they’re really testing: Whether you can systematically investigate environment differences rather than guessing or blaming the CI platform.
Strong answer covers: Check Node.js version parity between local and CI; look for tests relying on timing (fake timers with jest.useFakeTimers() / vi.useFakeTimers() are a common fix); check for shared state between test files that passes in serial locally but breaks in Jest’s parallel workers; inspect the CI logs for the specific error message rather than re-running blind. Mention --runInBand as a diagnostic step to isolate parallelism as the cause.
A developer on your team has written 60 test files where jest.mock() is used to mock almost every dependency, including simple utility functions. The suite shows 90% coverage and all tests pass, but a production bug still slipped through. How do you explain what went wrong and what you’d change?
What they’re really testing: Whether you understand the difference between test coverage as a metric and test coverage as genuine confidence in the software.
Strong answer covers: Over-mocking means tests are verifying that mocks behave like mocks, not that real code works; coverage numbers count executed lines, not meaningful assertions. Fix approach: trace the production bug back to the code path, show which mock hid it, introduce MSW to intercept at the network layer for the component tests that touch the API, and let that pattern replace the worst offenders gradually rather than rewriting all 60 files at once. Reference that this pattern commonly surfaces in NZ fintech or health-sector projects where test suites look mature but real incidents still occur.
How would you structure a Jest or Vitest test suite for a React application that handles personally identifiable information — for example, a patient portal at a NZ health provider?
What they’re really testing: Whether you think about data privacy and test safety at an architectural level, not just whether you can write a describe block.
Strong answer covers: Never use real PII in test fixtures — generate synthetic data with a library like @faker-js/faker seeded for repeatability; use MSW to return controlled fake responses at the network layer so no real API or database is touched; store no PII in __snapshots__ files that get committed to git. Mention the NZ Privacy Act 2020 obligation to minimise data collection and that test data seeded with real names or NHI numbers could constitute a data breach if the repo is ever leaked.
Learn more
Unit vs Integration: The Architecture Decision
The Testing Pyramid
The testing pyramid is not just a metaphor — it describes cost and speed. Unit tests sit at the base: they are fast (milliseconds each), cheap to write, and run in complete isolation. Integration tests sit in the middle: they are slower because they wire real components together, but they catch bugs that unit tests cannot. End-to-end tests sit at the top: they are the slowest and most expensive, but they verify real user journeys against a real browser. Jest and Vitest operate at the bottom two layers.
The Critical Architectural Mistake
The most common mistake teams make is unit-testing UI components by mocking everything around them. Consider a test that mocks fetch, mocks the React component's child components, and mocks the state store — then asserts that the mocked state was called with the mocked data. That test is not testing your software. It is testing that your mocks behave like mocks. When a real bug appears — a mismatched API field, a missing loading state, an uncaught error — those tests will stay green while users see broken screens.
The warning sign: if your test file has more lines of jest.mock() / vi.mock() than actual assertions, you are over-mocking.
The Right Boundary
Draw the boundary at the network layer, not at the component boundary. Use Mock Service Worker (MSW) to intercept HTTP requests at the network level and return controlled responses. This lets your component, your state management, your error handlers, and your rendering logic all run as they would in production — only the actual HTTP call is intercepted. The component does not know it is in a test. That is the right level of isolation.
What to Test at Each Layer
| Layer | What to Test | Tool |
|---|---|---|
| Unit | Pure logic with no side effects: date formatters, currency converters, input validators, reducers, state machines, business logic functions | Jest / Vitest (no mocks needed) |
| Integration | React/Vue components with real API calls, form submission flows, loading and error states, error boundary behaviour | Jest / Vitest + MSW (intercept at network) |
| E2E | Complete user journeys, cross-page flows, real authentication, payment flows, file uploads | Playwright / Cypress (real browser) |
A practical rule: if the function you are testing takes inputs and returns an output with no network calls, no DOM, and no shared state, unit test it with no mocks. If it touches a component that calls an API, integration-test it with MSW. If it spans multiple pages or requires a logged-in session, E2E-test it with Playwright.
Vitest vs Jest in 2026
When to Choose Vitest
Choose Vitest when your project is Vite-based: Nuxt 3, SvelteKit, Astro, or a modern React setup using Vite instead of webpack. The key advantage is that Vitest reuses your existing Vite config and transformation pipeline. There is no separate Babel config to maintain, no separate TypeScript transformer to configure, and no version mismatch between how your dev server handles imports and how your tests handle imports. What runs in the browser runs in tests — the same plugins, the same path aliases, the same environment variables.
Vitest is also meaningfully faster on large codebases. Because it uses esbuild for transformation (like Vite), it avoids the Babel overhead that slows Jest down on TypeScript-heavy projects.
When to Choose Jest
Choose Jest when your project uses webpack or Create React App, when your team has deep Jest ecosystem knowledge and tooling already built around it, or when you rely on complex mocking patterns with jest.mock(). Jest's ecosystem is larger — more third-party matchers, more integrations, more Stack Overflow answers. For teams on legacy Angular or older React setups, Jest is already embedded in the CI pipeline and developer muscle memory. Switching for its own sake is rarely worth the disruption.
Migrating from Jest to Vitest
The migration is mostly mechanical: replace import { describe, it, expect } from '@jest/globals' with import { describe, it, expect } from 'vitest', replace jest.fn() with vi.fn(), replace jest.mock() with vi.mock(). The API surface is intentionally compatible.
The one gotcha that trips teams up: mock hoisting. Jest automatically hoists jest.mock() calls to the top of the file so they run before imports. Vitest hoists vi.mock() too, but the behaviour differs slightly in edge cases — particularly when your mock factory references variables defined in the test file. If you have tests that rely on mock hoisting in non-obvious ways, test those files individually before declaring the migration complete.
The NZ Landscape in 2026
On greenfield projects — new SaaS products, government digital transformation projects, startup MVPs — Vitest is increasingly the default choice. Teams starting fresh with Vite have no reason to pull in Jest's additional configuration overhead. On legacy projects — existing Angular codebases, older React setups that pre-date Vite, enterprise Node.js APIs — Jest is usually already embedded and switching carries real risk with little reward. The practical advice for NZ testers: be comfortable with both. Know which config file to look for (jest.config.ts vs vitest.config.ts), know how to run a single test file, and know how to read the coverage report. The syntax inside test files is nearly identical either way.