Snapshot
Aprenda sobre Snapshots em vídeo na Vue SchoolTestes de snapshots são uma ferramenta muito útil sempre que você quiser garantir que a saída de suas funções não mude inesperadamente.
Ao usar snapshots, o Vitest captura um snapshot do valor fornecido e o compara com um arquivo de referência de snapshot armazenado junto com o teste. O teste falhará se os dois snapshots não corresponderem: isso pode indicar uma mudança inesperada no código, ou que o snapshot de referência precisa ser atualizado para a nova versão do resultado.
Usar Snapshots
Para gerar um snapshot de um valor, você pode usar o toMatchSnapshot()
da API expect()
:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchSnapshot();
});
Na primeira vez que este teste é executado, o Vitest cria um arquivo de snapshot que se parece com isto:
// Vitest Snapshot v1, https://www.getbook.com/pt-br/book/vitest-1/guide/snapshot
exports['toUpperCase 1'] = '"FOOBAR"';
O artefato de snapshot deve ser commitado junto com as alterações de código e revisado como parte do seu processo de revisão de código. Nas execuções subsequentes dos testes, o Vitest comparará a saída renderizada com o snapshot anterior. Se eles corresponderem, o teste passará. Caso contrário, o test runner pode ter encontrado um bug no seu código que precisa ser corrigido, ou a implementação mudou e o snapshot precisa ser atualizado.
WARNING
Ao usar Snapshots com testes assíncronos concorrentes, o expect
do Contexto de Teste local deve ser usado para assegurar a detecção correta do teste.
Snapshots Inline
Similarmente, você pode usar o toMatchInlineSnapshot()
para armazenar o snapshot inline dentro do próprio arquivo de teste.
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot();
});
Em vez de criar um arquivo de snapshot, o Vitest alterará o arquivo de teste diretamente para atualizar o snapshot, inserindo-o como uma string:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot('"FOOBAR"');
});
Isso permite que você veja a saída esperada diretamente sem precisar alternar entre arquivos.
WARNING
Ao usar Snapshots com testes assíncronos concorrentes, o expect
do Contexto de Teste local deve ser usado para assegurar a detecção correta do teste.
Atualizando Snapshots
Quando o valor recebido não corresponde ao snapshot, o teste falha e exibe a diferença entre eles. Quando a alteração do snapshot é esperada, você pode querer atualizar o snapshot para o estado atual.
No modo watch, você pode pressionar a tecla u
no terminal para atualizar diretamente o snapshot que falhou.
Ou você pode usar a opção --update
ou -u
na CLI para fazer o Vitest atualizar os snapshots.
vitest -u
Snapshots de Arquivo
Ao chamar toMatchSnapshot()
, todos os snapshots são armazenados em um arquivo de snapshot formatado. Isso significa que precisamos escapar certos caracteres (especificamente as aspas duplas "
e o acento grave `
) na string do snapshot. Além disso, você pode perder o destaque de sintaxe do conteúdo do snapshot (se estiverem em alguma linguagem).
Por isso, introduzimos toMatchFileSnapshot()
para comparar explicitamente com um arquivo. Isso permite atribuir qualquer extensão de arquivo ao arquivo de snapshot, tornando-os mais legíveis.
import { expect, it } from 'vitest';
it('render basic', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./test/basic.output.html');
});
Ele comparará com o conteúdo de ./test/basic.output.html
e pode ser reescrito com a flag --update
.
Snapshots de Imagem
Também é possível capturar snapshots de imagens usando jest-image-snapshot
.
npm i -D jest-image-snapshot
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png')).toMatchImageSnapshot();
});
Serializador Personalizado
Você pode adicionar sua própria lógica para modificar a forma como seus snapshots são serializados. Assim como o Jest, o Vitest possui serializadores padrão para tipos nativos do JavaScript, elementos HTML, ImmutableJS e elementos React.
Você pode adicionar explicitamente um serializador personalizado usando a API expect.addSnapshotSerializer
.
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` é uma função que serializa um valor usando plugins existentes.
return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
});
Também suportamos a opção snapshotSerializers para adicionar serializadores personalizados implicitamente.
import { SnapshotSerializer } from 'vitest';
export default {
serialize(val, config, indentation, depth, refs, printer) {
// `printer` é uma função que serializa um valor usando plugins existentes.
return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
} satisfies SnapshotSerializer;
import { defineConfig } from 'vite';
export default defineConfig({
test: {
snapshotSerializers: ['path/to/custom-serializer.ts'],
},
});
Depois de adicionar um teste como este:
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
};
expect(bar).toMatchSnapshot();
});
Você obterá o seguinte snapshot:
Pretty foo: Object {
"x": 1,
"y": 2,
}
Estamos usando o pretty-format
do Jest para serializar os snapshots. Você pode ler mais sobre isso aqui: pretty-format.
Diferença do Jest
O Vitest oferece uma funcionalidade de Snapshot quase compatível com o Jest, com algumas exceções:
1. O cabeçalho do comentário no arquivo de snapshot é diferente {#_1-comment-header-in-the-snapshot-file-is-different}
- // Jest Snapshot v1, https://goo.gl/fbAQLP
+ // Vitest Snapshot v1, https://www.getbook.com/pt-br/book/vitest-1/guide/snapshot
Isso não afeta a funcionalidade, mas pode impactar a diferença no seu commit ao migrar do Jest.
2. printBasicPrototype
tem como padrão false
{#_2-printbasicprototype-is-default-to-false}
Tanto os snapshots do Jest quanto os do Vitest são baseados em pretty-format
. No Vitest, printBasicPrototype
é false
por padrão, proporcionando uma saída de snapshot mais limpa. Já no Jest <29.0.0, o padrão é true
.
import { expect, test } from 'vitest';
test('snapshot', () => {
const bar = [
{
foo: 'bar',
},
];
// no Jest
expect(bar).toMatchInlineSnapshot(`
Array [
Object {
"foo": "bar",
},
]
`);
// no Vitest
expect(bar).toMatchInlineSnapshot(`
[
{
"foo": "bar",
},
]
`);
});
Acreditamos que este é um padrão mais razoável para legibilidade e para a experiência geral do desenvolvedor (DX). Se você ainda preferir o comportamento do Jest, pode alterar sua configuração:
// vitest.config.js
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true,
},
},
});
3. O sinal de maior >
é usado como separador em vez de dois pontos :
para mensagens personalizadas {#_3-chevron-is-used-as-a-separator-instead-of-colon-for-custom-messages}
Para maior legibilidade, o Vitest usa o sinal de maior >
como separador (em vez de dois pontos :
) quando uma mensagem personalizada é passada durante a criação de um arquivo de snapshot.
Para o seguinte exemplo de código de teste:
test('toThrowErrorMatchingSnapshot', () => {
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingSnapshot('hint');
});
No Jest, o snapshot será:
exports[`toThrowErrorMatchingSnapshot: hint 1`] = `"error"`;
No Vitest, o snapshot equivalente será:
exports[`toThrowErrorMatchingSnapshot > hint 1`] = `[Error: error]`;
4. O snapshot padrão de Error
é diferente para toThrowErrorMatchingSnapshot
e toThrowErrorMatchingInlineSnapshot
{#_4-default-error-snapshot-is-different-for-tothrowerrormatchingsnapshot-and-tothrowerrormatchinginlinesnapshot}
import { expect, test } from 'vitest';
test('snapshot', () => {
// no Jest e Vitest
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`);
// O Jest captura o `Error.message` para a instância `Error`
// O Vitest imprime o mesmo valor que toMatchInlineSnapshot
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingInlineSnapshot(`"error"`);
}).toThrowErrorMatchingInlineSnapshot(`[Error: error]`);
});