Skip to content

I test snapshot salvano l'output di un valore e lo confrontano con le future esecuzioni dei test. Questo e particolarmente utile per componenti UI, oggetti complessi o qualsiasi output che deve rimanere coerente.

Snapshot Base

I test snapshot sono scritti usando il matcher .toMatchSnapshot():

test.ts
ts
import { test, expect } from "bun:test";

test("snap", () => {
  expect("foo").toMatchSnapshot();
});

La prima volta che questo test viene eseguito, l'argomento di expect sara serializzato e scritto in un file speciale snapshot in una directory __snapshots__ accanto al file di test.

File Snapshot

Dopo aver eseguito il test sopra, Bun creera:

directory structure
text
your-project/
├── snap.test.ts
└── __snapshots__/
    └── snap.test.ts.snap

Il file snapshot contiene:

__snapshots__/snap.test.ts.snap
ts
// Bun Snapshot v1, https://bun.com/docs/test/snapshots

exports[`snap 1`] = `"foo"`;

Nelle esecuzioni successive, l'argomento viene confrontato con lo snapshot su disco.

Aggiornare gli Snapshot

Gli snapshot possono essere rigenerati con il seguente comando:

bash
bun test --update-snapshots

Questo e utile quando:

  • Hai intenzionalmente cambiato l'output
  • Stai aggiungendo nuovi test snapshot
  • L'output previsto e legittimamente cambiato

Snapshot Inline

Per valori piu piccoli, puoi usare snapshot inline con .toMatchInlineSnapshot(). Questi snapshot sono memorizzati direttamente nel tuo file di test:

test.ts
ts
import { test, expect } from "bun:test";

test("snapshot inline", () => {
  // Prima esecuzione: lo snapshot sara inserito automaticamente
  expect({ hello: "world" }).toMatchInlineSnapshot();
});

Dopo la prima esecuzione, Bun aggiorna automaticamente il tuo file di test:

test.ts
ts
import { test, expect } from "bun:test";

test("snapshot inline", () => {
  expect({ hello: "world" }).toMatchInlineSnapshot(`
{
  "hello": "world",
}
`);
});

Usare gli Snapshot Inline

  1. Scrivi il tuo test con .toMatchInlineSnapshot()
  2. Esegui il test una volta
  3. Bun aggiorna automaticamente il tuo file di test con lo snapshot 4Nelle esecuzioni successive, il valore sara confrontato con lo snapshot inline

Gli snapshot inline sono particolarmente utili per valori piccoli e semplici dove e utile vedere l'output atteso direttamente nel file di test.

Snapshot di Errori

Puoi anche snapshotare i messaggi di errore usando .toThrowErrorMatchingSnapshot() e .toThrowErrorMatchingInlineSnapshot():

test.ts
ts
import { test, expect } from "bun:test";

test("snapshot di errore", () => {
  expect(() => {
    throw new Error("Qualcosa e andato storto");
  }).toThrowErrorMatchingSnapshot();

  expect(() => {
    throw new Error("Un altro errore");
  }).toThrowErrorMatchingInlineSnapshot();
});

Dopo l'esecuzione, la versione inline diventa:

test.ts
ts
test("snapshot di errore", () => {
  expect(() => {
    throw new Error("Qualcosa e andato storto");
  }).toThrowErrorMatchingSnapshot();

  expect(() => {
    throw new Error("Un altro errore");
  }).toThrowErrorMatchingInlineSnapshot(`"Un altro errore"`);
});

Uso Avanzato degli Snapshot

Oggetti Complessi

Gli snapshot funzionano bene con oggetti nidificati complessi:

test.ts
ts
import { test, expect } from "bun:test";

test("snapshot oggetto complesso", () => {
  const user = {
    id: 1,
    name: "John Doe",
    email: "john@example.com",
    profile: {
      age: 30,
      preferences: {
        theme: "dark",
        notifications: true,
      },
    },
    tags: ["developer", "javascript", "bun"],
  };

  expect(user).toMatchSnapshot();
});

Snapshot di Array

Gli array sono anche adatti per i test snapshot:

test.ts
ts
import { test, expect } from "bun:test";

test("snapshot array", () => {
  const numbers = [1, 2, 3, 4, 5].map(n => n * 2);
  expect(numbers).toMatchSnapshot();
});

Snapshot dell'Output di Funzioni

Snapshot dell'output delle funzioni:

test.ts
ts
import { test, expect } from "bun:test";

function generateReport(data: any[]) {
  return {
    total: data.length,
    summary: data.map(item => ({ id: item.id, name: item.name })),
    timestamp: "2024-01-01", // Fisso per i test
  };
}

test("generazione report", () => {
  const data = [
    { id: 1, name: "Alice", age: 30 },
    { id: 2, name: "Bob", age: 25 },
  ];

  expect(generateReport(data)).toMatchSnapshot();
});

Snapshot di Componenti React

Gli snapshot sono particolarmente utili per i componenti React:

test.ts
tsx
import { test, expect } from "bun:test";
import { render } from "@testing-library/react";

function Button({ children, variant = "primary" }) {
  return <button className={`btn btn-${variant}`}>{children}</button>;
}

test("snapshot componenti Button", () => {
  const { container: primary } = render(<Button>Click me</Button>);
  const { container: secondary } = render(<Button variant="secondary">Cancel</Button>);

  expect(primary.innerHTML).toMatchSnapshot();
  expect(secondary.innerHTML).toMatchSnapshot();
});

Property Matcher

Per valori che cambiano tra le esecuzioni dei test (come timestamp o ID), usa i property matcher:

test.ts
ts
import { test, expect } from "bun:test";

test("snapshot con valori dinamici", () => {
  const user = {
    id: Math.random(), // Questo cambia ad ogni esecuzione
    name: "John",
    createdAt: new Date().toISOString(), // Anche questo cambia
  };

  expect(user).toMatchSnapshot({
    id: expect.any(Number),
    createdAt: expect.any(String),
  });
});

Lo snapshot memorizzera:

snapshot file
txt
exports[`snapshot with dynamic values 1`] = `
{
  "createdAt": Any<String>,
  "id": Any<Number>,
  "name": "John",
}
`;

Serializzatori Personalizzati

Puoi personalizzare come gli oggetti vengono serializzati negli snapshot:

test.ts
ts
import { test, expect } from "bun:test";

// Serializzatore personalizzato per oggetti Date
expect.addSnapshotSerializer({
  test: val => val instanceof Date,
  serialize: val => `"${val.toISOString()}"`,
});

test("serializzatore personalizzato", () => {
  const event = {
    name: "Meeting",
    date: new Date("2024-01-01T10:00:00Z"),
  };

  expect(event).toMatchSnapshot();
});

Best Practices

Mantieni gli Snapshot Piccoli

test.ts
ts
// Buono: snapshot focalizzati
test("formattazione nome utente", () => {
  const formatted = formatUserName("john", "doe");
  expect(formatted).toMatchInlineSnapshot(`"John Doe"`);
});

// Evita: snapshot enormi che sono difficili da revisionare
test("rendering intera pagina", () => {
  const page = renderEntirePage();
  expect(page).toMatchSnapshot(); // Questo potrebbe essere migliaia di righe
});

Usa Nomi Descrittivi dei Test

test.ts
ts
// Buono: chiaro cosa rappresenta lo snapshot
test("formatta valuta con simbolo USD", () => {
  expect(formatCurrency(99.99)).toMatchInlineSnapshot(`"$99.99"`);
});

// Evita: non chiaro cosa viene testato
test("test di formattazione", () => {
  expect(format(99.99)).toMatchInlineSnapshot(`"$99.99"`);
});

Gruppa Snapshot Correlati

test.ts
ts
import { describe, test, expect } from "bun:test";

describe("componente Button", () => {
  test("variante primary", () => {
    expect(render(<Button variant="primary">Click</Button>))
      .toMatchSnapshot();
  });

  test("variante secondary", () => {
    expect(render(<Button variant="secondary">Cancel</Button>))
      .toMatchSnapshot();
  });

  test("stato disabled", () => {
    expect(render(<Button disabled>Disabled</Button>))
      .toMatchSnapshot();
  });
});

Gestisci i Dati Dinamici

test.ts
ts
// Buono: normalizza i dati dinamici
test("formato risposta API", () => {
  const response = {
    data: { id: 1, name: "Test" },
    timestamp: Date.now(),
    requestId: generateId(),
  };

  expect({
    ...response,
    timestamp: "TIMESTAMP",
    requestId: "REQUEST_ID",
  }).toMatchSnapshot();
});

// O usa i property matcher
test("risposta API con matcher", () => {
  const response = getApiResponse();

  expect(response).toMatchSnapshot({
    timestamp: expect.any(Number),
    requestId: expect.any(String),
  });
});

Gestire gli Snapshot

Revisionare i Cambiamenti degli Snapshot

Quando gli snapshot cambiano, revisionali attentamente:

bash
# Vedi cosa e cambiato
git diff __snapshots__/

# Aggiorna se i cambiamenti sono intenzionali
bun test --update-snapshots

# Committa gli snapshot aggiornati
git add __snapshots__/
git commit -m "Update snapshots dopo cambiamenti UI"

Pulire gli Snapshot Inutilizzati

Bun avvisera degli snapshot inutilizzati:

warning
txt
Warning: 1 unused snapshot found:
  my-test.test.ts.snap: "old test that no longer exists 1"

Rimuovi gli snapshot inutilizzati cancellandoli dai file snapshot o eseguendo i test con i flag di pulizia se disponibili.

Organizzare File Snapshot Grandi

Per progetti grandi, considera di organizzare i test per mantenere i file snapshot gestibili:

directory structure
text
tests/
├── components/
│   ├── Button.test.tsx
│   └── __snapshots__/
│       └── Button.test.tsx.snap
├── utils/
│   ├── formatters.test.ts
│   └── __snapshots__/
│       └── formatters.test.ts.snap

Risoluzione dei Problemi

Fallimenti degli Snapshot

Quando gli snapshot falliscono, vedrai un diff:

diff
diff
- Expected
+ Received

  Object {
-   "name": "John",
+   "name": "Jane",
  }

Cause comuni:

  • Cambiamenti intenzionali (aggiorna con --update-snapshots)
  • Cambiamenti non intenzionali (fix del codice)
  • Dati dinamici (usa i property matcher)
  • Differenze ambientali (normalizza i dati)

Differenze di Piattaforma

Sii consapevole delle differenze specifiche della piattaforma:

test.ts
ts
// I percorsi potrebbero differire tra Windows/Unix
test("operazioni file", () => {
  const result = processFile("./test.txt");

  expect({
    ...result,
    path: result.path.replace(/\\/g, "/"), // Normalizza i percorsi
  }).toMatchSnapshot();
});

Bun a cura di www.bunjs.com.cn