Vitest Tutorial
A practical guide to Vitest, the testing framework built for modern JavaScript projects. Setup, syntax, mocking, coverage, and how it differs from Jest in ways that matter.
What you'll learn
- ✓What Vitest is and why it exists
- ✓How to write and run your first tests
- ✓How mocking and spies work
- ✓How to measure coverage
- ✓Migration notes from Jest
Prerequisites
- •Basic JavaScript or TypeScript
Vitest is a testing framework that feels like Jest but runs through Vite. It is fast, ESM-native, and integrates cleanly with TypeScript without the configuration drama. This tutorial gets you productive in an afternoon.
What and Why
Vitest is a unit test runner designed to share its toolchain with Vite. That means the same module resolution, the same plugins, the same transforms. Tests run through the same pipeline as your application, so you stop maintaining two parallel configurations.
The reason it caught on is partly speed and partly ergonomics. Hot reload of tests during development is genuinely useful. Native support for TypeScript, JSX, and ESM means less setup. The API is intentionally close to Jest, so migrating from an existing test suite is mostly cosmetic.
Mental Model
Picture Vitest as a thin layer over Vite. When you run a test, Vite transforms the file, Vitest runs it in a worker, and the results stream back to a watcher. The runner uses workers in parallel so independent files do not block each other. State between tests in different files is isolated unless you explicitly share it.
If you have used Jest, the keywords are familiar: describe, it, expect, beforeEach. The difference is that the transform pipeline is yours, not the runner’s, so weird build issues mostly disappear.
Hands-on Example
Install and write a first test.
npm install -D vitest
// math.js
export const add = (a, b) => a + b;
// math.test.js
import { describe, it, expect, vi } from 'vitest';
import { add } from './math.js';
describe('add', () => {
it('sums two numbers', () => {
expect(add(2, 3)).toBe(5);
});
});
describe('mocking', () => {
it('replaces a dependency', () => {
const logger = { info: vi.fn() };
logger.info('hello');
expect(logger.info).toHaveBeenCalledWith('hello');
});
});
Run with npx vitest. The watcher reruns only the affected files when you save.
test file change
|
v
[Vite transform] -> TS / JSX / ESM
|
v
[worker pool] -> isolated module graph per file
|
v
[reporter] -> watch UI / terminal
|
v
coverage + summary Add coverage with vitest --coverage after installing @vitest/coverage-v8.
Common Pitfalls
The first pitfall is mixing Jest globals and Vitest globals in the same project. They look identical but come from different packages. Pick one and remove the other from your dependencies.
The second is forgetting that ESM is the default. Modules are not magically reset between files the way they were in Jest’s CommonJS world. Use vi.resetModules() when you actually need a clean module graph.
The third is over-mocking. Vitest makes it easy to stub everything, and a test suite that stubs too much stops verifying real behaviour. Mock the boundary you do not own and run the rest for real.
Practical Tips
Run Vitest in UI mode with vitest --ui during development. The web interface shows the dependency graph and lets you click into individual tests, which is faster than scrolling terminal output.
Co-locate tests next to the source they cover. The convention foo.ts plus foo.test.ts keeps related code together and makes deletions cleaner.
Treat snapshot tests as a backup, not a default. They catch broad regressions but rot quickly. Prefer assertions on specific properties.
Wrap-up
Vitest is what testing in a Vite-based project should feel like: fast, native, and out of your way. If you are starting a new project today there is little reason to reach for an older runner.
Related articles
- Testing End-to-End Testing with Playwright: A Practical Tutorial
Learn how to write reliable end-to-end tests with Playwright, including selectors, fixtures, auto-waiting, and patterns that avoid flakiness.
- Testing Snapshot Tests Explained
Learn how snapshot tests work, when they shine, and how to keep them maintainable instead of letting them rot into noise.
- JavaScript TypeScript Generics Introduction
An approachable introduction to TypeScript generics — learn type parameters, constraints, defaults, and how to build reusable type-safe functions and components.
- Testing Property-Based Testing: An Introduction
Stop writing one example per test. Property-based testing generates inputs for you and finds the edge cases you would never think to write.