Skip to content

Las pruebas de snapshot guardan la salida de un valor y la comparan con ejecuciones futuras de pruebas. Esto es particularmente útil para componentes UI, objetos complejos o cualquier salida que necesite permanecer consistente.

Snapshots básicos

Las pruebas de snapshot se escriben usando el matcher .toMatchSnapshot():

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

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

La primera vez que se ejecuta esta prueba, el argumento a expect se serializará y escribirá en un archivo de snapshot especial en un directorio __snapshots__ junto al archivo de prueba.

Archivos de snapshot

Después de ejecutar la prueba anterior, Bun creará:

estructura de directorio
text
tu-proyecto/
├── snap.test.ts
└── __snapshots__/
    └── snap.test.ts.snap

El archivo de snapshot contiene:

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

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

En ejecuciones futuras, el argumento se compara con el snapshot en disco.

Actualizar snapshots

Los snapshots pueden regenerarse con el siguiente comando:

bash
bun test --update-snapshots

Esto es útil cuando:

  • Has cambiado intencionalmente la salida
  • Estás agregando nuevas pruebas de snapshot
  • La salida esperada ha cambiado legítimamente

Snapshots en línea

Para valores más pequeños, puedes usar snapshots en línea con .toMatchInlineSnapshot(). Estos snapshots se almacenan directamente en tu archivo de prueba:

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

test("snapshot en línea", () => {
  // Primera ejecución: el snapshot se insertará automáticamente
  expect({ hello: "world" }).toMatchInlineSnapshot();
});

Después de la primera ejecución, Bun actualiza automáticamente tu archivo de prueba:

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

test("snapshot en línea", () => {
  expect({ hello: "world" }).toMatchInlineSnapshot(`
{
  "hello": "world",
}
`);
});

Usar snapshots en línea

  1. Escribe tu prueba con .toMatchInlineSnapshot()
  2. Ejecuta la prueba una vez
  3. Bun actualiza automáticamente tu archivo de prueba con el snapshot
  4. En ejecuciones posteriores, el valor se comparará con el snapshot en línea

Los snapshots en línea son particularmente útiles para valores pequeños y simples donde es útil ver la salida esperada directamente en el archivo de prueba.

Snapshots de errores

También puedes hacer snapshot de mensajes de error usando .toThrowErrorMatchingSnapshot() y .toThrowErrorMatchingInlineSnapshot():

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

test("snapshot de error", () => {
  expect(() => {
    throw new Error("Algo salió mal");
  }).toThrowErrorMatchingSnapshot();

  expect(() => {
    throw new Error("Otro error");
  }).toThrowErrorMatchingInlineSnapshot();
});

Después de ejecutar, la versión en línea se convierte en:

test.ts
ts
test("snapshot de error", () => {
  expect(() => {
    throw new Error("Algo salió mal");
  }).toThrowErrorMatchingSnapshot();

  expect(() => {
    throw new Error("Otro error");
  }).toThrowErrorMatchingInlineSnapshot(`"Otro error"`);
});

Uso avanzado de snapshots

Objetos complejos

Los snapshots funcionan bien con objetos anidados complejos:

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

test("snapshot de objeto complejo", () => {
  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();
});

Snapshots de arrays

Los arrays también son adecuados para pruebas de snapshot:

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

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

Snapshots de salida de funciones

Haz snapshot de la salida de funciones:

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", // Fijo para pruebas
  };
}

test("generación de informe", () => {
  const data = [
    { id: 1, name: "Alice", age: 30 },
    { id: 2, name: "Bob", age: 25 },
  ];

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

Snapshots de componentes React

Los snapshots son particularmente útiles para componentes 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("snapshots de componente Button", () => {
  const { container: primary } = render(<Button>Haz clic aquí</Button>);
  const { container: secondary } = render(<Button variant="secondary">Cancelar</Button>);

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

Property Matchers

Para valores que cambian entre ejecuciones de pruebas (como marcas de tiempo o IDs), usa property matchers:

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

test("snapshot con valores dinámicos", () => {
  const user = {
    id: Math.random(), // Esto cambia en cada ejecución
    name: "John",
    createdAt: new Date().toISOString(), // Esto también cambia
  };

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

El snapshot almacenará:

archivo de snapshot
txt
exports[`snapshot con valores dinámicos 1`] = `
{
  "createdAt": Any<String>,
  "id": Any<Number>,
  "name": "John",
}
`;

Serializadores personalizados

Puedes personalizar cómo se serializan los objetos en snapshots:

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

// Serializador personalizado para objetos Date
expect.addSnapshotSerializer({
  test: val => val instanceof Date,
  serialize: val => `"${val.toISOString()}"`,
});

test("serializador personalizado", () => {
  const event = {
    name: "Reunión",
    date: new Date("2024-01-01T10:00:00Z"),
  };

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

Mejores prácticas

Mantener snapshots pequeños

test.ts
ts
// Bueno: Snapshots enfocados
test("formateo de nombre de usuario", () => {
  const formatted = formatUserName("john", "doe");
  expect(formatted).toMatchInlineSnapshot(`"John Doe"`);
});

// Evitar: Snapshots enormes difíciles de revisar
test("renderizado de página completa", () => {
  const page = renderEntirePage();
  expect(page).toMatchSnapshot(); // Esto podría ser miles de líneas
});

Usar nombres de prueba descriptivos

test.ts
ts
// Bueno: Claro qué representa el snapshot
test("formatea moneda con símbolo USD", () => {
  expect(formatCurrency(99.99)).toMatchInlineSnapshot(`"$99.99"`);
});

// Evitar: No claro qué se está probando
test("prueba de formato", () => {
  expect(format(99.99)).toMatchInlineSnapshot(`"$99.99"`);
});

Agrupar snapshots relacionados

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

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

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

  test("estado deshabilitado", () => {
    expect(render(<Button disabled>Deshabilitado</Button>))
      .toMatchSnapshot();
  });
});

Manejar datos dinámicos

test.ts
ts
// Bueno: Normalizar datos dinámicos
test("formato de respuesta de API", () => {
  const response = {
    data: { id: 1, name: "Test" },
    timestamp: Date.now(),
    requestId: generateId(),
  };

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

// O usar property matchers
test("respuesta de API con matchers", () => {
  const response = getApiResponse();

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

Gestionar snapshots

Revisar cambios de snapshots

Cuando los snapshots cambian, revísalos cuidadosamente:

bash
# Ver qué cambió
git diff __snapshots__/

# Actualizar si los cambios son intencionales
bun test --update-snapshots

# Confirmar los snapshots actualizados
git add __snapshots__/
git commit -m "Actualizar snapshots después de cambios de UI"

Limpiar snapshots no usados

Bun advertirá sobre snapshots no usados:

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

Elimina snapshots no usados eliminándolos de los archivos de snapshot o ejecutando pruebas con banderas de limpieza si están disponibles.

Organizar archivos de snapshot grandes

Para proyectos grandes, considera organizar pruebas para mantener los archivos de snapshot manejables:

estructura de directorio
text
tests/
├── components/
│   ├── Button.test.tsx
│   └── __snapshots__/
│       └── Button.test.tsx.snap
├── utils/
│   ├── formatters.test.ts
│   └── __snapshots__/
│       └── formatters.test.ts.snap

Solución de problemas

Fallos de snapshot

Cuando los snapshots fallan, verás un diff:

diff
diff
- Expected
+ Received

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

Causas comunes:

  • Cambios intencionales (actualiza con --update-snapshots)
  • Cambios no intencionales (arregla el código)
  • Datos dinámicos (usa property matchers)
  • Diferencias de entorno (normaliza los datos)

Diferencias de plataforma

Ten en cuenta diferencias específicas de plataforma:

test.ts
ts
// Las rutas pueden diferir entre Windows/Unix
test("operaciones de archivo", () => {
  const result = processFile("./test.txt");

  expect({
    ...result,
    path: result.path.replace(/\\/g, "/"), // Normalizar rutas
  }).toMatchSnapshot();
});

Bun por www.bunjs.com.cn editar