Ir para o conteúdo principal
Versão: 29.7

Testes de Snapshot

Tradução Beta Não Oficial

Esta página foi traduzida por PageTurner AI (beta). Não é oficialmente endossada pelo projeto. Encontrou um erro? Reportar problema →

Testes de snapshot são uma ferramenta extremamente útil quando você quer garantir que sua interface não mude inesperadamente.

Um caso de teste de snapshot típico renderiza um componente de UI, captura um snapshot e o compara com um arquivo de snapshot de referência armazenado junto ao teste. O teste falha se os snapshots não coincidirem: seja porque a mudança foi inesperada, ou porque o snapshot de referência precisa ser atualizado para a nova versão do componente.

Testes de Snapshot com Jest

Uma abordagem similar pode ser aplicada ao testar componentes React. Em vez de renderizar a interface gráfica - o que exigiria construir todo o aplicativo - você pode usar um renderizador de teste para gerar rapidamente um valor serializável para sua árvore React. Veja este exemplo de teste para um componente Link:

import renderer from 'react-test-renderer';
import Link from '../Link';

it('renders correctly', () => {
const tree = renderer
.create(<Link page="http://www.facebook.com">Facebook</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});

Na primeira execução desse teste, o Jest cria um arquivo de snapshot com este aspecto:

exports[`renders correctly 1`] = `
<a
className="normal"
href="http://www.facebook.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Facebook
</a>
`;

O artefato de snapshot deve ser commitado junto das mudanças de código e revisado no seu processo de code review. O Jest usa o pretty-format para tornar os snapshots legíveis durante revisões. Nas execuções seguintes, o Jest compara a saída renderizada com o snapshot anterior. Se coincidirem, o teste passa. Se não, ou o test runner encontrou um bug no seu código (no componente <Link> neste caso) que precisa ser corrigido, ou a implementação mudou e o snapshot precisa ser atualizado.

"message": "nota"

O snapshot tem escopo restrito aos dados que você renderiza - no nosso exemplo, o componente <Link> com a prop page. Isso significa que mesmo se outro arquivo (como App.js) tiver props faltantes no componente <Link>, o teste ainda passará, pois o teste não conhece o uso do <Link>, estando limitado ao Link.js. Além disso, renderizar o mesmo componente com props diferentes em outros testes de snapshot não afeta o primeiro, pois os testes não têm conhecimento mútuo.

"message": "informação"

Mais informações sobre como o teste de snapshot funciona e por que o criamos estão no post de lançamento. Recomendamos ler este artigo para entender quando usar snapshot testing. Também sugerimos assistir a este vídeo da egghead sobre Snapshot Testing com Jest.

Atualizando Snapshots

É fácil identificar quando um teste de snapshot falha após a introdução de um bug. Nesse caso, basta corrigir o problema para que os testes passem novamente. Mas vamos discutir quando um teste de snapshot falha devido a uma mudança intencional na implementação.

Um exemplo seria se mudássemos intencionalmente o endereço apontado pelo componente Link.

// Updated test case with a Link to a different address
it('renders correctly', () => {
const tree = renderer
.create(<Link page="http://www.instagram.com">Instagram</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});

Nessa situação, o Jest exibirá esta saída:

Como atualizamos o componente para apontar para outro endereço, é natural esperar mudanças no snapshot. Nosso teste falha porque o snapshot do componente atualizado não coincide mais com o artefato armazenado.

Para resolver, precisamos atualizar nossos artefatos de snapshot. Execute o Jest com um flag que solicite a regeneração dos snapshots:

jest --updateSnapshot

Aceite as alterações executando o comando acima. Você também pode usar a flag equivalente de caractere único -u para regenerar os snapshots, se preferir. Isso regenerará os artefatos de snapshot para todos os testes de snapshot que falharam. Se tivéssemos outros testes de snapshot falhando devido a um bug não intencional, precisaríamos corrigir o bug antes de regenerar os snapshots para evitar registrar snapshots do comportamento problemático.

Se quiser limitar quais casos de teste de snapshot serão regenerados, você pode passar a flag adicional --testNamePattern para regravar snapshots apenas para os testes que correspondem ao padrão.

Você pode experimentar essa funcionalidade clonando o exemplo de snapshot, modificando o componente Link e executando o Jest.

Modo Interativo de Snapshots

Snapshots com falha também podem ser atualizados interativamente no modo watch:

Ao entrar no Modo Interativo de Snapshots, o Jest guiará você pelos snapshots com falha, um teste por vez, dando oportunidade para revisar a saída com erro.

A partir daqui, você pode escolher atualizar aquele snapshot ou pular para o próximo:

Ao terminar, o Jest mostrará um resumo antes de retornar ao modo watch:

Snapshots Inline

Snapshots inline se comportam de forma idêntica aos snapshots externos (arquivos .snap), exceto que os valores são gravados automaticamente de volta no código-fonte. Isso significa que você obtém os benefícios dos snapshots gerados automaticamente sem precisar alternar para um arquivo externo para verificar se o valor correto foi gravado.

Exemplo:

Primeiro, escreva um teste chamando .toMatchInlineSnapshot() sem argumentos:

it('renders correctly', () => {
const tree = renderer
.create(<Link page="https://example.com">Example Site</Link>)
.toJSON();
expect(tree).toMatchInlineSnapshot();
});

Na próxima execução do Jest, tree será avaliado e um snapshot será gravado como argumento de toMatchInlineSnapshot:

it('renders correctly', () => {
const tree = renderer
.create(<Link page="https://example.com">Example Site</Link>)
.toJSON();
expect(tree).toMatchInlineSnapshot(`
<a
className="normal"
href="https://example.com"
onMouseEnter={[Function]}
onMouseLeave={[Function]}
>
Example Site
</a>
`);
});

É só isso! Você pode até atualizar os snapshots com --updateSnapshot ou usando a tecla u no modo --watch.

Por padrão, o Jest cuida da gravação dos snapshots no seu código-fonte. Porém, se você usa prettier no projeto, o Jest detectará e delegará o trabalho para o prettier (respeitando sua configuração).

Matchers de Propriedades

Frequentemente existem campos em objetos que você quer incluir no snapshot que são gerados (como IDs e Datas). Se tentar fazer snapshot desses objetos, eles forçarão a falha do snapshot em toda execução:

it('will fail every time', () => {
const user = {
createdAt: new Date(),
id: Math.floor(Math.random() * 20),
name: 'LeBron James',
};

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

// Snapshot
exports[`will fail every time 1`] = `
{
"createdAt": 2018-05-19T23:36:09.816Z,
"id": 3,
"name": "LeBron James",
}
`;

Para esses casos, o Jest permite fornecer um matcher assimétrico para qualquer propriedade. Esses matchers são verificados antes do snapshot ser gravado ou testado, e então salvos no arquivo de snapshot no lugar do valor recebido:

it('will check the matchers and pass', () => {
const user = {
createdAt: new Date(),
id: Math.floor(Math.random() * 20),
name: 'LeBron James',
};

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

// Snapshot
exports[`will check the matchers and pass 1`] = `
{
"createdAt": Any<Date>,
"id": Any<Number>,
"name": "LeBron James",
}
`;

Qualquer valor fornecido que não seja um matcher será verificado exatamente e salvo no snapshot:

it('will check the values and pass', () => {
const user = {
createdAt: new Date(),
name: 'Bond... James Bond',
};

expect(user).toMatchSnapshot({
createdAt: expect.any(Date),
name: 'Bond... James Bond',
});
});

// Snapshot
exports[`will check the values and pass 1`] = `
{
"createdAt": Any<Date>,
"name": 'Bond... James Bond',
}
`;

Se o caso envolver uma string e não um objeto, você precisará substituir manualmente a parte aleatória antes de testar o snapshot.
Você pode usar por exemplo replace() e expressões regulares.

const randomNumber = Math.round(Math.random() * 100);
const stringWithRandomData = `<div id="${randomNumber}">Lorem ipsum</div>`;
const stringWithConstantData = stringWithRandomData.replace(/id="\d+"/, 123);
expect(stringWithConstantData).toMatchSnapshot();

Outras formas de fazer isso são usando o serializador de snapshots ou mockando a biblioteca responsável por gerar a parte aleatória do código que você está snapshotando.

Melhores Práticas

Os snapshots são uma ferramenta fantástica para identificar alterações inesperadas na interface do seu aplicativo — seja essa interface uma resposta de API, UI, logs ou mensagens de erro. Como em qualquer estratégia de teste, existem algumas práticas recomendadas que você deve conhecer e diretrizes a seguir para usá-los efetivamente.

1. Trate snapshots como código

Comite snapshots e revise-os como parte do seu processo regular de code review. Isso significa tratar snapshots como qualquer outro tipo de teste ou código no seu projeto.

Garanta que seus snapshots sejam legíveis mantendo-os focados, curtos e usando ferramentas que reforcem essas convenções estilísticas.

Como mencionado anteriormente, o Jest usa pretty-format para tornar os snapshots legíveis, mas você pode achar útil introduzir ferramentas adicionais, como eslint-plugin-jest com sua opção no-large-snapshots, ou snapshot-diff com seu recurso de comparação de snapshots de componentes, para promover asserções curtas e focadas.

O objetivo é facilitar a revisão de snapshots em pull requests e combater o hábito de regenerar snapshots quando os testes falham, em vez de examinar as causas raiz das falhas.

2. Testes devem ser determinísticos

Seus testes devem ser determinísticos. Executar os mesmos testes várias vezes em um componente que não mudou deve produzir os mesmos resultados sempre. Você é responsável por garantir que seus snapshots gerados não incluam dados específicos de plataforma ou outros dados não determinísticos.

Por exemplo, se você tem um componente Clock que usa Date.now(), o snapshot gerado será diferente sempre que o teste for executado. Nesse caso, podemos mockar o método Date.now() para retornar um valor consistente:

Date.now = jest.fn(() => 1_482_363_367_071);

Agora, sempre que o teste de snapshot for executado, Date.now() retornará 1482363367071 consistentemente. Isso resultará no mesmo snapshot sendo gerado independentemente de quando o teste for executado.

3. Use nomes descritivos para snapshots

Sempre busque usar nomes descritivos para testes e/ou snapshots. Os melhores nomes descrevem o conteúdo esperado do snapshot. Isso facilita que revisores verifiquem os snapshots durante a revisão e que qualquer pessoa identifique se um snapshot desatualizado representa o comportamento correto antes de atualizar.

Por exemplo, compare:

exports[`<UserName /> should handle some test case`] = `null`;

exports[`<UserName /> should handle some other test case`] = `
<div>
Alan Turing
</div>
`;

Com:

exports[`<UserName /> should render null`] = `null`;

exports[`<UserName /> should render Alan Turing`] = `
<div>
Alan Turing
</div>
`;

Como o último descreve exatamente o esperado na saída, fica mais claro identificar quando está errado:

exports[`<UserName /> should render null`] = `
<div>
Alan Turing
</div>
`;

exports[`<UserName /> should render Alan Turing`] = `null`;

Perguntas Frequentes

Snapshots são gravados automaticamente em sistemas de CI?

Não. A partir do Jest 20, snapshots não são gravados automaticamente quando o Jest é executado em CI sem passar explicitamente --updateSnapshot. Espera-se que todos os snapshots façam parte do código executado em CI, e como novos snapshots passam automaticamente, eles não devem passar em uma execução de testes em CI. Recomenda-se sempre comitar todos os snapshots e mantê-los no versionamento.

Arquivos de snapshot devem ser commitados?

Sim. Todos os arquivos de snapshot devem ser commitados junto com os módulos que cobrem e seus testes. Eles devem ser considerados parte do teste, semelhante a qualquer outra asserção no Jest. Na verdade, snapshots representam o estado dos módulos fonte em qualquer momento. Assim, quando os módulos fonte são modificados, o Jest pode identificar o que mudou em relação à versão anterior. Também fornecem contexto adicional durante code reviews, permitindo que revisores analisem melhor suas alterações.

Testes de snapshot funcionam apenas com componentes React?

Componentes React e React Native são excelentes casos de uso para testes de snapshot. Porém, snapshots podem capturar qualquer valor serializável e devem ser usados sempre que o objetivo for verificar se uma saída está correta. O repositório do Jest contém vários exemplos testando a própria saída do Jest, os resultados da biblioteca de asserções e mensagens de log de diversas partes do código. Veja um exemplo de snapshot da saída do CLI no repositório.

Qual a diferença entre snapshot testing e teste de regressão visual?

Snapshot testing e testes de regressão visual são duas abordagens distintas para testar UIs, com propósitos diferentes. Ferramentas de regressão visual capturam screenshots de páginas web e comparam as imagens pixel a pixel. Já no snapshot testing, os valores são serializados, armazenados em arquivos de texto e comparados usando algoritmos de diff. Existem diferentes compensações a considerar, e listamos as razões para criar snapshot testing no blog do Jest.

O snapshot testing substitui testes unitários?

Snapshot testing é apenas uma das mais de 20 asserções incluídas no Jest. Seu objetivo não é substituir testes unitários existentes, mas agregar valor adicional e simplificar os testes. Em alguns cenários, pode eliminar a necessidade de testes unitários para funcionalidades específicas (ex: componentes React), mas também podem funcionar em conjunto.

Qual o desempenho do snapshot testing em velocidade e tamanho de arquivos?

Jest foi reescrito com foco em desempenho, e snapshot testing não é exceção. Como snapshots são armazenados em arquivos de texto, essa abordagem é rápida e confiável. O Jest gera um novo arquivo para cada arquivo de teste que usa o matcher toMatchSnapshot. O tamanho é bastante compacto: como referência, todos os snapshots da base de código do Jest somam menos de 300 KB.

Como resolver conflitos em arquivos de snapshot?

Arquivos de snapshot devem sempre refletir o estado atual dos módulos que cobrem. Portanto, ao mesclar branches com conflitos em snapshots, você pode resolver manualmente ou atualizar o snapshot executando o Jest e analisando o resultado.

É possível aplicar TDD com snapshot testing?

Embora seja possível escrever snapshots manualmente, isso geralmente não é prático. Snapshots ajudam a detectar alterações na saída dos módulos testados, não servindo como guia para o design inicial do código.

Cobertura de código funciona com snapshot testing?

Sim, da mesma forma que com qualquer outro teste.