# Il test runner nativo di Node.js: vale la pena lasciare Jest?

> Guida pratica a node:test, il test runner integrato in Node.js: setup senza dipendenze, sintassi base, mock, coverage e cosa manca ancora rispetto a Jest.

- Autore / Author: Diego Betto (https://diegobetto.com/about/)
- Pubblicato / Published: 2026-09-04
- URL: https://diegobetto.com/node-test-runner-vs-jest/
- Tag: nodejs, testing, javascript

Node.js ha un test runner integrato dalla versione 18 (stabile dalla 20), ma la stragrande maggioranza dei progetti continua a installare Jest o Vitest per default, spesso senza chiedersi se serva davvero. Il modulo `node:test` è maturato abbastanza da essere un'alternativa concreta per una buona fetta di progetti — non per tutti.

### Setup: zero dipendenze

Non serve installare nulla. `node:test` fa parte del runtime:

```js
// somma.test.js
import { test } from "node:test";
import assert from "node:assert/strict";
import { somma } from "./somma.js";

test("somma due numeri positivi", () => {
  assert.strictEqual(somma(2, 3), 5);
});

test("somma con un negativo", () => {
  assert.strictEqual(somma(5, -2), 3);
});
```

```bash
node --test
```

Nessun file di configurazione, nessun transform da configurare per ESM — `node --test` cerca automaticamente i file che matchano pattern comuni (`*.test.js`, `*.spec.js`, cartelle `test/`) ed esegue tutto in parallelo per default.

### `describe`/`it`, hook e test asincroni

La sintassi è deliberatamente vicina a quella di Jest/Mocha, per rendere la migrazione meno dolorosa:

```js
import { describe, it, before, after } from "node:test";
import assert from "node:assert/strict";

describe("UtenteRepository", () => {
  let repo;

  before(() => {
    repo = new UtenteRepository();
  });

  after(async () => {
    await repo.chiudi();
  });

  it("trova un utente per id", async () => {
    const utente = await repo.trovaPerId(1);
    assert.equal(utente.nome, "Diego");
  });

  it("lancia se l'id non esiste", async () => {
    await assert.rejects(() => repo.trovaPerId(999), /non trovato/);
  });
});
```

`assert.rejects`/`assert.doesNotReject` coprono i casi async che in Jest richiedono `expect(...).rejects.toThrow()` — l'API è più verbosa in alcuni punti, ma copre gli stessi scenari.

### Mock integrati, senza librerie esterne

Dalla 20 in poi, `node:test` include un sistema di mock nativo (`t.mock`), che copre buona parte di quello per cui prima serviva `jest.fn()` o `sinon`:

```js
import { test } from "node:test";
import assert from "node:assert/strict";

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

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

  assert.strictEqual(invia.mock.callCount(), 1);
  assert.deepEqual(invia.mock.calls[0].arguments, ["diego@example.com"]);
});
```

`t.mock.method()` permette anche di sostituire temporaneamente un metodo su un oggetto esistente (l'equivalente di `jest.spyOn`), e viene ripristinato automaticamente alla fine del test.

> `node --test --watch` riesegue solo i test dei file modificati, con lo stesso spirito di `jest
> --watch`. Utile durante lo sviluppo, anche se il feedback su quali test sono stati re-triggerati è
> più essenziale rispetto all'interfaccia di Jest.

### Coverage senza pacchetti aggiuntivi

```bash
node --test --experimental-test-coverage
```

Il coverage è generato internamente (usa la strumentazione V8, non `istanbul`/`nyc`), stampato in console per default. Per un report HTML navigabile serve comunque configurare un reporter esterno — qui Jest, con `--coverage` pronto all'uso e integrazioni editor mature, resta più comodo.

### Cosa manca ancora rispetto a Jest

- **Snapshot testing**: `node:test` non ha un equivalente nativo di `toMatchSnapshot()`. Per progetti che si affidano molto agli snapshot (tipicamente componenti UI), è un'assenza che pesa.
- **Ambiente DOM/jsdom integrato**: Jest configura `jsdom` con un flag; con `node:test` va aggiunto manualmente. Per testare componenti React/Vue è un lavoro in più che Jest (o Vitest) risparmiano.
- **Matcher espressivi**: `assert.strictEqual`/`assert.deepEqual` coprono i casi comuni, ma l'ecosistema di matcher di `expect` (`toBeCloseTo`, `toContainEqual`, matcher custom) è più ricco e leggibile per asserzioni complesse.
- **Ecosistema di plugin ed editor integration**: Jest (e Vitest) hanno anni di plugin, estensioni VS Code e integrazioni CI mature. `node:test` è più recente e la superficie di tooling attorno è ancora più piccola.

### Quando ha senso migrare

Per librerie e pacchetti Node.js senza dipendenze da DOM — utility, client HTTP, logica di business pura, script CLI — `node:test` è spesso sufficiente e rimuove una dipendenza intera dal progetto, con avvio più veloce (nessun transform da caricare) e zero configurazione da mantenere. Per progetti frontend con componenti da renderizzare e snapshot da confrontare, o suite di test già grandi e stabili su Jest, la migrazione raramente vale il tempo che costa: il beneficio principale di `node:test` è partire senza dipendenze, non convertire quello che già funziona.

Per il resto dell'ecosistema Node lato sicurezza e manutenzione, vale la pena tenere d'occhio anche gli aggiornamenti descritti nell'articolo sulle [security release di Node.js](https://diegobetto.com/nodejs-security-release-2026/).
