# Vitest: cos'è, come si installa e come usarlo con React 19

> Guida pratica a Vitest: installazione, configurazione, come testare componenti React 19 (incluso useActionState), mock con vi, coverage e confronto con Jest e node:test.

- Autore / Author: Diego Betto (https://diegobetto.com/about/)
- Pubblicato / Published: 2026-09-22
- URL: https://diegobetto.com/vitest-guida-react-19/
- Tag: testing, vitest, react, javascript

Vitest è il test runner sviluppato dal team di Vite, pensato per condividere la stessa pipeline di trasformazione del bundler: stesso `vite.config.ts`, stessi plugin, stessi alias, niente configurazione parallela da mantenere. L'API è deliberatamente compatibile con quella di Jest (`describe`, `it`, `expect`), quindi la migrazione da un progetto Jest esistente è quasi sempre un cambio di importazioni, non una riscrittura.

### Installazione

```bash
npm install -D vitest
```

Per progetti React serve anche un ambiente DOM e le utility di testing:

```bash
npm install -D jsdom @testing-library/react @testing-library/jest-dom @testing-library/user-event
```

La configurazione va nello stesso file di Vite (o in un `vitest.config.ts` separato, se preferisci tenerla isolata dal build):

```ts
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  test: {
    environment: "jsdom",
    globals: true,
    setupFiles: "./vitest.setup.ts",
  },
});
```

```ts
// vitest.setup.ts
import "@testing-library/jest-dom/vitest";
```

`globals: true` evita di dover importare `describe`/`it`/`expect` in ogni file (stesso comportamento a cui è abituato chi arriva da Jest).

### Un primo test

```js
// somma.test.js
import { expect, test } from "vitest";
import { somma } from "./somma.js";

test("somma due numeri positivi", () => {
  expect(somma(2, 3)).toBe(5);
});
```

```bash
npx vitest
```

Di default parte in watch mode e riesegue solo i test toccati dai file modificati, sfruttando lo stesso grafo di dipendenze che Vite usa per l'HMR — è la differenza pratica più evidente rispetto a Jest: qui non c'è un transform separato da ricalcolare, i file già trasformati restano in cache.

### Testare un componente React 19

Esempio con `useActionState`, l'hook introdotto in React 19 per gestire lo stato derivato da una form action:

```tsx
// LikeButton.tsx
import { useActionState } from "react";

async function likeAction(prevState: number) {
  await new Promise((resolve) => setTimeout(resolve, 50));
  return prevState + 1;
}

export function LikeButton() {
  const [likes, formAction, isPending] = useActionState(likeAction, 0);

  return (
    <form action={formAction}>
      <button type="submit" disabled={isPending}>
        {isPending ? "..." : `Mi piace (${likes})`}
      </button>
    </form>
  );
}
```

```tsx
// LikeButton.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { expect, test } from "vitest";
import { LikeButton } from "./LikeButton";

test("incrementa i like dopo il submit", async () => {
  const user = userEvent.setup();
  render(<LikeButton />);

  await user.click(screen.getByRole("button"));

  expect(await screen.findByText("Mi piace (1)")).toBeInTheDocument();
});
```

`findByText` è necessario perché `formAction` è asincrona: il bottone passa prima per lo stato `isPending`, e serve aspettare il re-render finale invece di leggere subito il DOM con `getByText`. Per approfondire le altre novità dell'hook system in React 19 (incluso `use()`), c'è l'articolo su [`use` vs `useContext`](https://diegobetto.com/react-use-vs-usecontext/).

### Mock con `vi`

```js
import { vi, test, expect } from "vitest";
import { registraUtente } from "./registraUtente.js";

test("chiama il servizio email con i parametri giusti", () => {
  const inviaEmail = vi.fn(() => true);

  registraUtente({ email: "diego@example.com" }, { inviaEmail });

  expect(inviaEmail).toHaveBeenCalledWith("diego@example.com");
});
```

`vi.spyOn()` sostituisce temporaneamente un metodo su un oggetto esistente, `vi.mock()` sostituisce un intero modulo (utile per isolare chiamate `fetch` o client HTTP) — stessa superficie concettuale di `jest.fn`/`jest.spyOn`/`jest.mock`, nomi diversi.

> `npx vitest --ui` apre una dashboard nel browser con l'elenco dei test, i risultati e un timeline
> dei singoli assert — utile per debuggare una suite grande senza leggere output di terminale.
> Richiede il pacchetto separato `@vitest/ui`.

### Coverage

```bash
npm install -D @vitest/coverage-v8
npx vitest run --coverage
```

Il coverage usa la stessa strumentazione V8 di Node, con report testuale in console e HTML navigabile generato in `coverage/` — pronto all'uso, senza configurare `istanbul`/`nyc` a parte.

### Vitest vs Jest vs `node:test`

|                                  | Vitest                         | Jest                                                | `node:test`          |
| -------------------------------- | ------------------------------ | --------------------------------------------------- | -------------------- |
| Config condivisa col build       | Sì (stesso `vite.config.ts`)   | No (config separata)                                | N/A                  |
| TypeScript/JSX senza setup extra | Sì                             | Serve `ts-jest`/`babel-jest`                        | Serve loader esterno |
| Ambiente DOM (jsdom)             | Sì, con un flag                | Sì, con un flag                                     | Manuale              |
| Snapshot testing                 | Sì                             | Sì                                                  | No                   |
| Dipendenze                       | Vite + eventuali plugin        | Nessuna extra (ma ecosistema Babel spesso presente) | Zero                 |
| Watch mode basato su             | Grafo di dipendenze Vite (HMR) | File system polling                                 | File system          |

Jest resta la scelta più matura per progetti non basati su Vite, con anni di plugin ed estensioni editor. `node:test` è l'opzione a costo zero per librerie senza dipendenze DOM, ma manca di snapshot testing e ambiente jsdom integrato — l'ho confrontato più in dettaglio nell'articolo su [`node:test` vs Jest](https://diegobetto.com/node-test-runner-vs-jest/). Vitest si posiziona nel mezzo: per progetti già su Vite, Astro, Next.js (in modalità Vite) o qualsiasi setup React/TypeScript moderno, è quasi sempre la scelta con meno attrito, perché riusa configurazione e velocità del bundler che il progetto ha già.

### Quando ha senso sceglierlo

Se il progetto usa già Vite (o un framework basato su Vite), Vitest evita di mantenere due pipeline di build separate e parte più veloce grazie al transform condiviso. Per progetti legacy su Webpack con una suite Jest grande e stabile, la migrazione raramente vale il tempo che costa — a meno che la lentezza dei test in CI sia già un problema concreto da risolvere.
