# Vitest: an introduction

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

Author: [Flavio Copes](https://flaviocopes.com/about/) | Published: 2026-08-04 | Updated: 2026-08-03 | Topics: [JavaScript](https://flaviocopes.com/tags/js/) | Canonical: https://flaviocopes.com/vitest/

If you write JavaScript or [TypeScript](https://flaviocopes.com/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](https://flaviocopes.com/vite-tutorial/). 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](https://flaviocopes.com/npm/) as a dev dependency:

```bash
npm i -D vitest
```

Create a small function to test:

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

Now write a test file next to it:

```js
// 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:

```bash
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`:

```bash
npx vitest run
```

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

```bash
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:

| Matcher | What 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:

```js
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:

```js
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:

```js
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:

```js
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:

```bash
npm i -D @vitest/coverage-v8
```

Run tests with coverage enabled:

```bash
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.
