Home > Blog > Desenvolvimento Web
Desenvolvimento Web
JavaScript
Programação

Snapshot Tests no Node.js

Atualizado em: 17 de agosto de 2026

Estação de trabalho usada no desenvolvimento de aplicações Node.js

Os Snapshot Tests no Node.js permitem serializar um valor e compará-lo com uma versão aprovada armazenada em arquivo. Esse tipo de teste é útil para objetos complexos, árvores, respostas formatadas, mensagens de erro, configurações e saídas textuais que seriam trabalhosas de escrever manualmente em dezenas de asserts.

O Node.js Test Runner possui suporte nativo a snapshots. A funcionalidade foi adicionada no Node.js 22.3.0 e deixou de ser experimental no Node.js 23.4.0. Os arquivos são gerados ou atualizados com a flag --test-update-snapshots e devem ser revisados antes de entrar no controle de versão.

Neste guia, você aprenderá a criar snapshots, atualizar arquivos, usar file snapshots, personalizar serialização e caminhos, evitar dados instáveis, revisar alterações no Git, testar erros e escolher quando asserts explícitos são melhores.

O que é um snapshot test?

Um snapshot test transforma um valor em texto e compara o resultado com uma referência conhecida. A documentação oficial de snapshot testing no Node.js apresenta a API atual. Para comparações explícitas, consulte a documentação oficial do módulo Assert.

Para a base da suíte, veja Node Test Runner. Para funções simuladas, consulte Mocks no Node.js Test Runner. O artigo sobre Cobertura de Testes no Node.js ajuda a medir caminhos executados.

Primeiro snapshot

const { test } = require('node:test');

test('serializa usuário', t => {
  t.assert.snapshot({
    id: 1,
    name: 'Ana',
    active: true
  });
});

Na primeira execução, o teste falha se o snapshot ainda não existe. Isso evita criar referências silenciosamente sem revisão.

Gerando o arquivo

node --test --test-update-snapshots

O runner cria ou atualiza o arquivo associado ao teste. O nome padrão costuma derivar do arquivo de teste com a extensão de snapshot definida pelo Test Runner.

Executando novamente

node --test

Agora o valor atual é comparado com a referência. Qualquer diferença faz o teste falhar.

Snapshots no controle de versão

Arquivos de snapshot devem ser commitados junto com os testes. Eles representam a saída aprovada e precisam passar pela mesma revisão de código.

Não adicione snapshots automaticamente sem olhar o diff. Uma atualização pode registrar um bug como novo comportamento esperado.

Revisando o diff

Ao ocorrer uma mudança:

  1. execute o teste sem atualizar;
  2. leia a diferença;
  3. confirme se a regra realmente mudou;
  4. ajuste o código ou o teste;
  5. atualize o snapshot conscientemente;
  6. revise o arquivo gerado no Git.

Vários snapshots no mesmo teste

test('gera diferentes formatos', t => {
  t.assert.snapshot(formatUser(user));
  t.assert.snapshot(formatOrder(order));
});

Cada assert precisa de identificação estável derivada da posição e do teste. Reordenar chamadas pode alterar o mapeamento. Prefira testes separados quando os valores representam comportamentos independentes.

Snapshots de strings

test('formata relatório', t => {
  const report = renderReport(data);
  t.assert.snapshot(report);
});

Strings multilinha, templates e mensagens formatadas são bons candidatos quando toda a saída importa.

Snapshots de objetos

Objetos grandes podem ser legíveis no arquivo, mas remova campos instáveis antes da comparação:

const normalized = {
  ...result,
  createdAt: '<DATE>',
  requestId: '<REQUEST_ID>'
};

t.assert.snapshot(normalized);

Dados instáveis

Campos que costumam gerar mudanças desnecessárias incluem:

  • timestamps;
  • UUIDs;
  • paths absolutos;
  • portas aleatórias;
  • ordem de propriedades não controlada;
  • stack traces;
  • versão do sistema operacional;
  • tempo de execução.

Normalize, injete valores determinísticos ou use asserts específicos.

Relógio controlado

test('gera data previsível', t => {
  t.mock.timers.enable({
    apis: ['Date'],
    now: new Date('2026-01-01T00:00:00Z')
  });

  t.assert.snapshot(createInvoice());
});

A API exata de timers depende da versão. Fixe o runtime no CI.

Ordenação de arrays

Quando a ordem não faz parte do contrato, ordene antes do snapshot:

const normalized = [...users].sort(
  (a, b) => a.id - b.id
);

t.assert.snapshot(normalized);

Quando a ordem é importante, não normalize: o snapshot deve detectar a mudança.

Ordenação de objetos

Objetos JavaScript possuem regras de ordem, mas dados vindos de bancos ou APIs podem variar. Crie uma representação estável com as propriedades relevantes.

File snapshots

O TestContext pode oferecer fileSnapshot() para comparar um valor com um arquivo específico:

test('gera arquivo de configuração', t => {
  const content = generateConfiguration();

  t.assert.fileSnapshot(
    content,
    './fixtures/expected-config.txt'
  );
});

Esse formato é útil quando o arquivo esperado também serve como fixture legível ou exemplo da documentação.

Snapshot versus file snapshot

  • snapshot: runner gerencia um arquivo por teste.
  • fileSnapshot: você escolhe o caminho do arquivo esperado.

Use file snapshot para artefatos completos, como HTML, SQL gerado ou configurações.

Paths seguros

Use caminhos dentro do repositório. Não permita que entrada externa determine onde o teste grava snapshots.

Personalizando o caminho

A API snapshot.setResolveSnapshotPath() permite mudar a localização:

const { snapshot } = require('node:test');
const path = require('node:path');

snapshot.setResolveSnapshotPath(testFile => {
  return path.join(
    path.dirname(testFile),
    '__snapshots__',
    `${path.basename(testFile)}.snapshot`
  );
});

O formato da função deve seguir a documentação da versão usada.

Configuração global

Registre a resolução antes de os snapshots serem usados. Um módulo de setup global pode centralizar a convenção em versões compatíveis.

Serializadores padrão

O Test Runner oferece configuração de serializers para transformar valores:

snapshot.setDefaultSnapshotSerializers([
  value => stableSerialize(value)
]);

O contrato do serializer deve ser consultado na versão atual. Ele precisa produzir saída determinística e legível.

Serializer customizado

Um serializer pode:

  • ordenar propriedades;
  • substituir dados variáveis;
  • formatar classes;
  • ocultar detalhes irrelevantes;
  • reduzir objetos enormes.

Não esconda campos que fazem parte do comportamento que o teste deveria proteger.

Segredos

Snapshots ficam no repositório e em artefatos de CI. Nunca inclua:

  • tokens;
  • senhas;
  • cookies;
  • chaves privadas;
  • dados pessoais;
  • URLs com credenciais.

Sanitize antes de chamar o assert.

Snapshots de erros

test('formata erro público', async t => {
  try {
    await service.execute();
  } catch (error) {
    t.assert.snapshot({
      name: error.name,
      code: error.code,
      message: error.message
    });
  }
});

Evite stack trace, porque paths e linhas mudam com frequência. Use assert.rejects() quando apenas o tipo ou código importa.

Snapshots de HTTP

Normalize headers variáveis e não registre Authorization:

const snapshotResponse = {
  status: response.status,
  headers: {
    'content-type': response.headers.get('content-type')
  },
  body: await response.json()
};

t.assert.snapshot(snapshotResponse);

Snapshots de banco

Um snapshot pode validar objetos retornados, mas não substitui testes de constraints, transações e SQL real. Use banco isolado para integração.

Snapshots de HTML

HTML gerado pode ser um bom candidato se a estrutura completa é relevante. Formate a saída para reduzir diferenças de whitespace sem esconder mudanças semânticas.

Snapshots de logs

Logs contêm timestamps e IDs. Em vez de capturar tudo, teste a estrutura:

t.assert.snapshot({
  level: log.level,
  event: log.event,
  fields: Object.keys(log.fields).sort()
});

Snapshots pequenos

Um snapshot deve caber na revisão humana. Arquivos com milhares de linhas tendem a ser atualizados sem leitura. Divida o comportamento ou use asserts direcionados.

Assert explícito

Para uma regra crítica, isto é melhor:

assert.equal(result.total, 125);
assert.equal(result.currency, 'BRL');

do que um snapshot do objeto inteiro, porque a intenção fica clara.

Quando usar snapshots?

  • saída textual complexa;
  • árvore de configuração;
  • objeto com muitos campos relevantes;
  • template ou serialização;
  • mensagem pública padronizada;
  • resultado de parser controlado.

Quando evitar?

  • objeto enorme;
  • dados aleatórios;
  • regra com dois valores simples;
  • integração que muda frequentemente;
  • conteúdo com segredos;
  • saída dependente de plataforma sem normalização.

Atualização em CI

O pipeline de verificação não deve executar --test-update-snapshots. Ele precisa falhar quando há diferença.

A atualização deve ocorrer localmente ou em fluxo explícito, seguida de revisão.

Script separado

{
  "scripts": {
    "test": "node --test",
    "test:update": "node --test --test-update-snapshots"
  }
}

Pull requests

O revisor deve analisar código e snapshots juntos. Uma mudança de snapshot sem mudança esperada de requisito é sinal de investigação.

Merge conflicts

Snapshots podem gerar conflitos quando vários testes compartilham o mesmo arquivo. Uma organização por arquivo de teste reduz colisões.

Renomeando testes

O identificador do snapshot pode depender do nome e da posição. Renomear ou mover testes pode gerar novas entradas. Remova referências órfãs após confirmar a alteração.

Testes concorrentes

Não faça vários testes escreverem o mesmo file snapshot ao mesmo tempo. Use arquivos distintos e atualização controlada.

Compatibilidade de plataforma

Normalize:

  • separadores de path;
  • finais de linha;
  • timezone;
  • ordem de diretórios;
  • mensagens específicas do sistema.

Windows e POSIX

const normalizedPath = value.replaceAll('\\', '/');

Faça isso apenas quando a diferença de separador não é o comportamento testado.

Locale

Datas e números formatados podem variar conforme locale. Informe locale e timezone explicitamente.

Desempenho

Serializar objetos muito grandes torna testes lentos. Meça e reduza o valor ao contrato relevante.

Cobertura

Snapshots podem executar muitas linhas, mas um único snapshot não garante cobertura de branches. Use diferentes casos de entrada.

Mocks

Controle relógio e serviços externos para obter saída determinística. Não use mocks para reimplementar toda a integração.

Testes de mutação

Mutation testing pode revelar snapshots permissivos demais, especialmente quando o serializer remove campos importantes.

Testando serializers

O serializer customizado também precisa de testes explícitos. Um bug nele pode alterar todos os snapshots ou esconder regressões.

Erros comuns

  • Atualizar sem revisar: bugs viram referência.
  • Incluir timestamps: testes falham sempre.
  • Snapshots gigantes: ninguém lê o diff.
  • Guardar segredos: dados vazam no Git.
  • Usar para regra simples: intenção fica escondida.
  • Atualizar no CI: regressões são aceitas automaticamente.
  • Não fixar locale: plataformas produzem saídas diferentes.

Boas práticas

  • Mantenha snapshots pequenos.
  • Normalize apenas instabilidade.
  • Revise todo diff.
  • Commit os arquivos.
  • Use asserts explícitos em regras críticas.
  • Separe o comando de atualização.
  • Proteja segredos.
  • Fixe timezone e locale.
  • Teste serializers.
  • Remova snapshots órfãos.

Conclusão

Os Snapshot Tests no Node.js oferecem uma forma nativa e estável de proteger saídas complexas no Test Runner. A referência é criada com --test-update-snapshots e comparada nas execuções seguintes.

Snapshots funcionam bem quando são determinísticos, pequenos e revisados. Eles não substituem asserts claros nem testes de integração. Com normalização cuidadosa, controle de segredos e atualização manual, tornam mudanças de estruturas e formatos fáceis de detectar sem transformar a suíte em uma coleção de arquivos aprovados sem leitura.

Os 10 Melhores Cursos de Programação de 2026

Descubra os melhores cursos de programação. Aprenda a escolher o curso ideal para iniciar ou avançar na carreira de desenvolvedor

POSTS RELACIONADOS

Ver todos

Seta para a direita