Skip to content

Teste de snapshot salva a saída de um valor e compara com execuções de teste futuras. Isso é particularmente útil para componentes de UI, objetos complexos ou qualquer saída que precise permanecer consistente.

Snapshots Básicos

Testes de snapshot são escritos usando o matcher .toMatchSnapshot():

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

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

A primeira vez que este teste é executado, o argumento para expect será serializado e escrito em um arquivo de snapshot especial em um diretório __snapshots__ ao lado do arquivo de teste.

Arquivos de Snapshot

Após executar o teste acima, o Bun criará:

estrutura de diretório
text
seu-projeto/
├── snap.test.ts
└── __snapshots__/
    └── snap.test.ts.snap

O arquivo de snapshot contém:

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

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

Em execuções futuras, o argumento é comparado com o snapshot em disco.

Atualizando Snapshots

Snapshots podem ser regenerados com o seguinte comando:

bash
bun test --update-snapshots

Isso é útil quando:

  • Você mudou intencionalmente a saída
  • Está adicionando novos testes de snapshot
  • A saída esperada mudou legitimamente

Snapshots Inline

Para valores menores, você pode usar snapshots inline com .toMatchInlineSnapshot(). Estes snapshots são armazenados diretamente no seu arquivo de teste:

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

test("snapshot inline", () => {
  // Primeira execução: snapshot será inserido automaticamente
  expect({ hello: "world" }).toMatchInlineSnapshot();
});

Após a primeira execução, o Bun atualiza automaticamente seu arquivo de teste:

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

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

Usando Snapshots Inline

  1. Escreva seu teste com .toMatchInlineSnapshot()
  2. Execute o teste uma vez
  3. O Bun atualiza automaticamente seu arquivo de teste com o snapshot
  4. Nas execuções subsequentes, o valor será comparado com o snapshot inline

Snapshots inline são particularmente úteis para valores pequenos e simples onde é útil ver a saída esperada diretamente no arquivo de teste.

Snapshots de Erro

Você também pode snapshotar mensagens de erro usando .toThrowErrorMatchingSnapshot() e .toThrowErrorMatchingInlineSnapshot():

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

test("snapshot de erro", () => {
  expect(() => {
    throw new Error("Algo deu errado");
  }).toThrowErrorMatchingSnapshot();

  expect(() => {
    throw new Error("Outro erro");
  }).toThrowErrorMatchingInlineSnapshot();
});

Após executar, a versão inline se torna:

test.ts
ts
test("snapshot de erro", () => {
  expect(() => {
    throw new Error("Algo deu errado");
  }).toThrowErrorMatchingSnapshot();

  expect(() => {
    throw new Error("Outro erro");
  }).toThrowErrorMatchingInlineSnapshot(`"Outro erro"`);
});

Uso Avançado de Snapshots

Objetos Complexos

Snapshots funcionam bem com objetos aninhados complexos:

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

test("snapshot de objeto complexo", () => {
  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 Array

Arrays também são adequados para teste 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 Saída de Função

Snapshot da saída de funções:

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", // Fixo para teste
  };
}

test("geração de relatório", () => {
  const data = [
    { id: 1, name: "Alice", age: 30 },
    { id: 2, name: "Bob", age: 25 },
  ];

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

Snapshots de Componentes React

Snapshots são particularmente úteis 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>Clique em mim</Button>);
  const { container: secondary } = render(<Button variant="secondary">Cancelar</Button>);

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

Property Matchers

Para valores que mudam entre execuções de teste (como timestamps ou IDs), use property matchers:

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

test("snapshot com valores dinâmicos", () => {
  const user = {
    id: Math.random(), // Isso muda a cada execução
    name: "John",
    createdAt: new Date().toISOString(), // Isso também muda
  };

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

O snapshot armazenará:

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

Serializadores Personalizados

Você pode personalizar como objetos são serializados em 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ão",
    date: new Date("2024-01-01T10:00:00Z"),
  };

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

Melhores Práticas

Mantenha Snapshots Pequenos

test.ts
ts
// Bom: Snapshots focados
test("formatação de nome de usuário", () => {
  const formatted = formatUserName("john", "doe");
  expect(formatted).toMatchInlineSnapshot(`"John Doe"`);
});

// Evite: Snapshots enormes difíceis de revisar
test("renderização de página inteira", () => {
  const page = renderEntirePage();
  expect(page).toMatchSnapshot(); // Isso poderia ser milhares de linhas
});

Use Nomes de Teste Descritivos

test.ts
ts
// Bom: Claro o que o snapshot representa
test("formata moeda com símbolo USD", () => {
  expect(formatCurrency(99.99)).toMatchInlineSnapshot(`"$99.99"`);
});

// Evite: Não claro o que está sendo testado
test("teste de formatação", () => {
  expect(format(99.99)).toMatchInlineSnapshot(`"$99.99"`);
});

Agrupe Snapshots Relacionados

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

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

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

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

Lide com Dados Dinâmicos

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

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

// Ou use property matchers
test("resposta de API com matchers", () => {
  const response = getApiResponse();

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

Gerenciando Snapshots

Revisando Mudanças de Snapshot

Quando snapshots mudam, revise-os cuidadosamente:

bash
# Ver o que mudou
git diff __snapshots__/

# Atualizar se mudanças são intencionais
bun test --update-snapshots

# Commitar snapshots atualizados
git add __snapshots__/
git commit -m "Atualizar snapshots após mudanças de UI"

Limpando Snapshots Não Utilizados

O Bun alertará sobre snapshots não utilizados:

aviso
txt
Aviso: 1 snapshot não utilizado encontrado:
  my-test.test.ts.snap: "teste antigo que não existe mais 1"

Remova snapshots não utilizados deletando-os dos arquivos de snapshot ou executando testes com flags de limpeza se disponíveis.

Organizando Arquivos de Snapshot Grandes

Para projetos grandes, considere organizar testes para manter arquivos de snapshot gerenciáveis:

estrutura de diretório
text
tests/
├── components/
│   ├── Button.test.tsx
│   └── __snapshots__/
│       └── Button.test.tsx.snap
├── utils/
│   ├── formatters.test.ts
│   └── __snapshots__/
│       └── formatters.test.ts.snap

Solução de Problemas

Falhas de Snapshot

Quando snapshots falham, você verá um diff:

diff
diff
- Esperado
+ Recebido

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

Causas comuns:

  • Mudanças intencionais (atualize com --update-snapshots)
  • Mudanças não intencionais (corrija o código)
  • Dados dinâmicos (use property matchers)
  • Diferenças de ambiente (normalize os dados)

Diferenças de Plataforma

Esteja ciente de diferenças específicas de plataforma:

test.ts
ts
// Caminhos podem diferir entre Windows/Unix
test("operações de arquivo", () => {
  const result = processFile("./test.txt");

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

Bun by www.bunjs.com.cn edit