Skip to content
FLAVIO COPES
flaviocopes.com

Vitest: an introduction

By

Vitest is the fast Vite-powered test runner with a Jest-compatible API, native ESM and TypeScript support, watch mode, and built-in mocking.

~~~

If you write JavaScript or TypeScript today, you need a test runner that understands modern modules. Vitest is what I reach for on new projects.

It’s a test runner built on Vite. Same config, same transforms, same plugins. If your app already runs on Vite — or Astro, or any modern setup that uses it — Vitest picks up that pipeline automatically.

The API feels like Jest. You get describe, it, expect, and a vi namespace for mocks. But Vitest runs native ESM and TypeScript without a separate transform step. No ts-jest. No Babel config just to run tests.

Vitest 4 is the current stable line as of mid-2026. Watch mode is fast. Coverage is built in. Monorepos get a projects config for running different test setups in one command.

Install and your first test

Install Vitest with npm as a dev dependency:

npm i -D vitest

Create a small function to test:

// sum.js
export function sum(a, b) {
  return a + b
}

Now write a test file next to it:

// sum.test.js
import { describe, it, expect } from 'vitest'
import { sum } from './sum.js'

describe('sum', () => {
  it('adds two numbers', () => {
    expect(sum(2, 3)).toBe(5)
  })
})

Run it:

npx vitest

Vitest finds files matching *.test.js or *.spec.js and runs them. You can use test() instead of it — same thing.

Watch mode and the UI

When you run npx vitest locally with no flags, watch mode is on by default. In CI, Vitest detects the environment and runs once instead. Change a file locally, save it, and the related tests re-run instantly.

Want a one-off run without watching? Use run:

npx vitest run

For a visual dashboard, install the UI package and launch it:

npm i -D @vitest/ui
npx vitest --ui

It opens in the browser and shows your test files, results, and errors in one place.

Common matchers

Vitest uses Jest-compatible matchers. These four cover most of what you need:

MatcherWhat it checks
toBe(5)Strict equality (===)
toEqual({ a: 1 })Deep equality for objects and arrays
toContain('apple')Array or string includes a value
toThrow('message')Function throws an error

Example with a shopping cart:

const items = ['apple', 'bread', 'milk']

expect(items).toContain('bread')
expect(items.length).toBe(3)

Async tests

Testing async code is straightforward. Just await inside the test:

it('fetches the cart total', async () => {
  const total = await getCartTotal(['apple', 'bread'])
  expect(total).toBe(8.50)
})

Vitest waits for the promise to settle before marking the test done. If the promise rejects and you didn’t expect it, the test fails.

Mocking with vi

Vitest puts mocks under the vi namespace. vi.fn() creates a spy you can track:

import { vi, expect, it } from 'vitest'

it('calls the discount function', () => {
  const applyDiscount = vi.fn((total) => total * 0.9)

  checkout(100, applyDiscount)

  expect(applyDiscount).toHaveBeenCalledWith(100)
})

To replace an entire module, use vi.mock() at the top of the file:

vi.mock('./api.js', () => ({
  fetchCart: vi.fn(() => Promise.resolve([])),
}))

That’s enough to get started. Vitest also supports spies, timers, and module hoisting — same patterns you’d use in Jest, just with vi instead of jest.

Coverage

Install the coverage provider:

npm i -D @vitest/coverage-v8

Run tests with coverage enabled:

npx vitest run --coverage

Vitest prints a summary in the terminal. You can also configure reporters and thresholds in vitest.config.ts if you want coverage gates in CI.

TypeScript

Write your tests in .ts files and Vitest handles them. No extra setup.

Rename sum.test.js to sum.test.ts, add types to your function, and run the same command. Vitest reads your Vite config (or its own vitest.config.ts) and compiles TypeScript on the fly.

For monorepos, the projects option in config lets you define separate test setups — different environments, different include patterns — and run them all with one npx vitest command.

My advice: if you’re starting a new JavaScript or TypeScript project in 2026, install Vitest before you write your second test file. It’s fast, the API is familiar, and if you’re already on Vite, there’s almost nothing to configure.

~~~

Related posts about js: