Skip to main content
Version: 26.x

Teste de Snapshot

Testes de snapshot são ferramentas bem úteis sempre que você desejar garantir que sua UI não seja alterada inesperadamente.

A typical snapshot test case renders a UI component, takes a snapshot, then compares it to a reference snapshot file stored alongside the test. O teste irá falhar se as duas imagens não coincidirem: quer a mudança seja inesperada, ou a captura de tela precisa ser atualizada para a nova versão do componente da UI.

Testes de Snapshot com Jest#

Uma abordagem semelhante pode ser tomada quando se trata de testar seus componentes React. Em vez de renderizar a interface gráfica do usuário, ou UI, que iria precisar construir o aplicativo inteiro, você pode usar um renderizador de teste para gerar rapidamente um valor serializável para sua árvore React. Considere este exemplo de teste para um simples componente de Link:

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

A primeira vez que esse teste é executado, Jest cria um arquivo de snapshot que se parece com isto:

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

O artefato do snapshot deve ser comitado (committed, em inglês) junto com as alterações de código, e revisado como parte de seu processo de revisão de código. Jest usa pretty-format para fazer snapshots legíveis durante a revisão do código. On subsequent test runs, Jest will compare the rendered output with the previous snapshot. Se eles coincidirem, o teste passará. If they don't match, either the test runner found a bug in your code (in the <Link> component in this case) that should be fixed, or the implementation has changed and the snapshot needs to be updated.

Note: The snapshot is directly scoped to the data you render – in our example the <Link /> component with page prop passed to it. This implies that even if any other file has missing props (Say, App.js) in the <Link /> component, it will still pass the test as the test doesn't know the usage of <Link /> component and it's scoped only to the Link.react.js. Além disso, renderizar o mesmo componente com propriedades diferentes em outros testes de snapshot não afetará o primeiro, como os testes não se conhecem.

More information on how snapshot testing works and why we built it can be found on the release blog post. Nós recomendamos ler este post no blog para obter uma boa base de quando você deve usar teste de snapshot. Também recomendamos assistir este vídeo no egghead sobre Testes de Snapshot com Jest.

Atualizando Snapshots#

É simples de detectar quando um snapshot falhar depois que um bug foi introduzido. Quando isso acontecer, vá em frente e corrija o problema e certifique-se de que seus testes de snapshot estão passando novamente. Agora, vamos falar sobre o caso de quando um teste de snapshot está falhando devido a uma alteração intencional da implementação.

Uma situação assim pode ocorrer se nós intencionalmente mudarmos o endereço que para onde o componente Link aponta em no nosso exemplo.

// Caso de teste atualizado com um Link para um endereço diferente
it('renderiza corretamente', () => {
const tree = renderer
.create(<Link page="http://www.instagram.com">Instagram</Link>)
.toJSON();
expect(tree).toMatchSnapshot();
});

Nesse caso, Jest irá imprimir esta saída:

Uma vez que acabamos de atualizar nosso componente para apontar para um endereço diferente, é razoável esperar mudanças no snapshot para este componente. Nosso caso de teste de snapshot está falhando porque o snapshot para nosso componente atualizado já não coincide com o artefato de snapshot para este caso de teste.

Para resolver esse problema, precisamos atualizar nossos artefatos de snapshot. Você pode executar Jest com uma flag que irá dizer para re-gerar snapshots:

jest --updateSnapshot

Vá em frente e aceite as alterações executando o comando acima. Você também pode usar o caractere único equivalente -u como flag para re-gerar snapshots, se preferir. Isso irá re-gerar artefatos de snapshot para todos testes de snapshot que falharam. Se tivéssemos mais algum teste de snapshot falhando devido a um bug não intencional, precisamos corrigir o bug antes de re-gerar snapshots para evitar a gravação de snapshot do bug.

Se você gostaria de limitar quais casos de teste snapshot devem ser gerados novamente, você pode passar uma flag adicional --testNamePattern para re-gravar snapshots somente para aqueles testes que correspondem ao padrão.

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

Modo de Snapshot Interativo#

Os snapshots que falharam também podem ser atualizados de forma interativa no modo "watch":

Uma vez que você entra o Modo de Snapshot Interativo, Jest irá passo a passo nos snapshots com falhas, um conjunto de teste por vez, e dar-lhe a oportunidade de revisar o resultado que falhou.

A partir daqui você pode optar por atualizar esse snapshot ou pular para o próximo:

Uma vez terminado, Jest irá fornecer-lhe um resumo antes de retornar para o modo "watch":

Inline Snapshots#

Inline snapshots se comportam de forma idêntica aos snapshots externos (arquivos .snap), exceto os valores de snapshot que são escritos automaticamente no código fonte. Isso significa que você pode obter os benefícios de snapshots gerados automaticamente sem precisar alternar para um arquivo externo para certificar-se de que o valor correto foi escrito.

Inline snapshots are powered by Prettier. To use inline snapshots you must have prettier installed in your project. Your Prettier configuration will be respected when writing to test files.

If you have prettier installed in a location where Jest can't find it, you can tell Jest how to find it using the "prettierPath" configuration property.

Example:

Primeiro, você escreve um teste, chamando .toMatchInlineSnapshot() sem argumentos:

it('renderiza corretamente', () => {
const tree = renderer
.create(<Link page="https://prettier.io">Prettier</Link>)
.toJSON();
expect(tree).toMatchInlineSnapshot();
});

Na próxima vez que você executar o Jest, tree será calculado, e um snapshot será escrito como um argumento para toMatchInlineSnapshot:

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

É tudo o que há nisso! Você pode até mesmo atualizar os snapshots com --updateSnapshot ou usando a chave u no modo --watch.

Property Matchers#

Often there are fields in the object you want to snapshot which are generated (like IDs and Dates). If you try to snapshot these objects, they will force the snapshot to fail on every run:

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`] = ` Object {
"createdAt": 2018-05-19T23:36:09.816Z,
"id": 3,
"name": "LeBron James",
}
`;

For these cases, Jest allows providing an asymmetric matcher for any property. These matchers are checked before the snapshot is written or tested, and then saved to the snapshot file instead of the received value:

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`] = `
Object {
"createdAt": Any<Date>,
"id": Any<Number>,
"name": "LeBron James",
}
`;

Qualquer valor dado que não combina será verificado exatamente e salvo no snapshot:

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`] = ` Object {
"createdAt": Any<Date>,
"id": Any<Number>,
"name": "LeBron James",
}
`;

Best Practices#

Snapshots are a fantastic tool for identifying unexpected interface changes within your application – whether that interface is an API response, UI, logs, or error messages. As with any testing strategy, there are some best-practices you should be aware of, and guidelines you should follow, in order to use them effectively.

1. Treat snapshots as code#

Commit snapshots and review them as part of your regular code review process. This means treating snapshots as you would any other type of test or code in your project.

Ensure that your snapshots are readable by keeping them focused, short, and by using tools that enforce these stylistic conventions.

As mentioned previously, Jest uses pretty-format to make snapshots human-readable, but you may find it useful to introduce additional tools, like eslint-plugin-jest with its no-large-snapshots option, or snapshot-diff with its component snapshot comparison feature, to promote committing short, focused assertions.

The goal is to make it easy to review snapshots in pull requests, and fight against the habit of regenerating snapshots when test suites fail instead of examining the root causes of their failure.

2. Tests should be deterministic#

Seus testes devem ser determinísticos. Running the same tests multiple times on a component that has not changed should produce the same results every time. Você é responsável por certificar-se de que seus snapshots gerados não incluem dados específicos de plataforma ou outros não determinísticos.

Por exemplo, se você tem um componente Clock que usa Date.now(), o snapshot gerado a partir deste componente será diferente cada vez que o caso de teste é executado. Neste caso podemos simular o método Date.now() para retornar um valor consistente toda vez que o teste é executado:

Date.now = jest.fn(() => 1482363367071);

Agora, toda vez que o caso de teste de snapshot é executado, Date.now() retornará consistentemente 1482363367071. Isso resultará no mesmo snapshot sendo gerado para este componente independentemente de quando o teste é executado.

3. Use descriptive snapshot names#

Always strive to use descriptive test and/or snapshot names for snapshots. The best names describe the expected snapshot content. This makes it easier for reviewers to verify the snapshots during review, and for anyone to know whether or not an outdated snapshot is the correct behavior before updating.

For example, compare:

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

Para:

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

Como a parte posterior descreve exatamente o que se espera na saída, é mais claro ver quando está errado:

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

Perguntas Frequentes#

Are snapshots written automatically on Continuous Integration (CI) systems?#

No, as of Jest 20, snapshots in Jest are not automatically written when Jest is run in a CI system without explicitly passing --updateSnapshot. Espera-se que todos os snapshots são parte do código que é executado em CI e como novos snapshots passam automaticamente, eles não devem passar um teste executado em um sistema de CI. É recomendável sempre dar commit em todos os snapshots e mantê-los no controle de versão.

Arquivos de snapshot devem ser comitados (committed, em inglês)?#

Sim, todos os arquivos de snapshot devem ser comitados juntamente com os módulos que eles cobrem e seus testes. Eles devem ser considerados parte de um teste, similar ao valor de qualquer outra verificação em Jest. Na verdade, snapshots representam o estado dos módulos fonte em qualquer ponto no tempo. Desta forma, quando os módulos fonte são modificados, Jest pode dizer o que mudou da versão anterior. Também pode fornecer um monte de contexto adicional durante a revisão do código na qual os revisores podem estudar melhor as alterações.

Teste de snapshot só funciona com componentes React?#

Componentes React e React Native são um bom caso de uso para testes de snapshot. No entanto, snapshots podem capturar qualquer valor que pode ser serializado e devem ser usados sempre que o objetivo é testar se a saída é correta. O repositório Jest contém muitos exemplos de testes da saída do próprio Jest, a saída da biblioteca de verificação do Jest, bem como mensagens de log de várias partes do código do Jest. Veja um exemplo de saída CLI de snapshotting no repositório do Jest.

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

Teste de snapshot e teste de regressão visual são duas maneiras distintas de testar interfaces de usuário, ou UIs, e eles servem para finalidades diferentes. Ferramentas de teste de regressão visual tiram screenshots de páginas da web e comparam as imagens resultantes pixel por pixel. Com testes de snapshot os valores são serializados, armazenados dentro de arquivos de texto e comparados usando um algoritmo de comparação. There are different trade-offs to consider and we listed the reasons why snapshot testing was built in the Jest blog.

Teste de snapshot substitui teste unitário?#

Teste de snapshot é apenas um das mais de 20 verificações que acompanham Jest. O objetivo do teste de snapshot não é substituir os testes unitários existentes, mas fornecer valor adicional e tornar os testes mais fáceis. Em alguns cenários, teste de snapshot podem potencialmente eliminar a necessidade de testes unitários para um determinado conjunto de funcionalidades (por exemplo, componentes React), mas também podem trabalhar juntos.

Qual é o desempenho do teste de snapshot no que diz respeito a velocidade e tamanho dos arquivos gerados?#

Jest foi reescrito tendo desempenho em mente, e teste de snapshot não é uma exceção. Como os snapshots são armazenados dentro de arquivos de texto, esta forma de teste é rápida e confiável. Jest gera um novo arquivo para cada arquivo de teste que invoca o matcher toMatchSnapshot. The size of the snapshots is pretty small: For reference, the size of all snapshot files in the Jest codebase itself is less than 300 KB.

Como eu resolvo conflitos dentro de arquivos de snapshot?#

Arquivos de snapshot sempre devem representar o estado atual dos módulos que estão cobrindo. Portanto, se você estiver dando merge em duas branches e encontra um conflito nos arquivos de snapshot, você pode resolver o conflito manualmente ou atualizar o arquivo de snapshot executando Jest e inspecionar o resultado.

É possível aplicar os princípios de desenvolvimento orientado a testes com teste de snapshot?#

Embora seja possível escrever arquivos de snapshot manualmente, isto geralmente não é amigável. Snapshots ajudam a descobrir se a saída dos módulos cobertos por testes foi alterada, ao invés de dar orientação para a concepção do código.

Cobertura de código funciona com testes de snapshot?#

Sim, assim como com qualquer outro teste.