Migawki
Naucz się testów migawkowych z wideo Vue SchoolTesty migawkowe to niezwykle przydatne narzędzie, gdy chcesz mieć pewność, że wynik działania Twoich funkcji nie zmienia się w sposób nieoczekiwany.
Podczas korzystania z migawek, Vitest tworzy migawkę danej wartości, a następnie porównuje ją z referencyjnym plikiem migawki przechowywanym obok testu. Test zakończy się niepowodzeniem, jeśli dwie migawki nie będą pasować: oznacza to, że zmiana jest albo nieoczekiwana, albo referencyjna migawka musi zostać zaktualizowana do nowej wersji wyniku.
Używanie migawek
Aby wykonać migawkę wartości, możesz użyć toMatchSnapshot()
z API expect()
:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchSnapshot();
});
Przy pierwszym uruchomieniu tego testu, Vitest tworzy plik migawki, który wygląda następująco:
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports['toUpperCase 1'] = '"FOOBAR"';
Plik migawki powinien być zatwierdzony w systemie kontroli wersji wraz ze zmianami kodu i przeglądany w ramach procesu code review. Podczas kolejnych uruchomień testów, Vitest porówna wyrenderowany wynik z poprzednią migawką. Jeśli są zgodne, test zakończy się pomyślnie. Jeśli nie są zgodne, oznacza to, że narzędzie do uruchamiania testów wykryło błąd w Twoim kodzie, który należy naprawić, albo implementacja uległa zmianie i migawka wymaga aktualizacji.
WARNING
Podczas używania migawek z asynchronicznymi testami współbieżnymi, należy użyć expect
z lokalnego kontekstu testowego, aby zapewnić prawidłowe wykrycie testu.
Migawki liniowe
Podobnie, możesz użyć toMatchInlineSnapshot()
do przechowywania migawki bezpośrednio w pliku testowym.
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot();
});
Zamiast tworzyć plik migawki, Vitest bezpośrednio zmodyfikuje plik testowy, aby zaktualizować migawkę jako string:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot('"FOOBAR"');
});
Pozwala to na bezpośrednie zobaczenie oczekiwanego wyniku bez konieczności przełączania się między różnymi plikami.
WARNING
Podczas używania migawek z asynchronicznymi testami współbieżnymi, należy użyć expect
z lokalnego kontekstu testowego, aby zapewnić prawidłowe wykrycie testu.
Aktualizowanie migawek
Gdy otrzymana wartość nie zgadza się z migawką, test kończy się niepowodzeniem i pokazuje różnicę między nimi. Gdy zmiana migawki jest oczekiwana, możesz chcieć zaktualizować migawkę do bieżącego stanu.
W trybie watch możesz nacisnąć klawisz u
w terminalu, aby bezpośrednio zaktualizować migawkę, która nie przeszła testu.
Lub możesz użyć flagi --update
lub -u
w CLI, aby Vitest zaktualizował migawki.
vitest -u
Migawki plików
Podczas wywoływania toMatchSnapshot()
, wszystkie migawki są przechowywane w sformatowanym pliku migawki. Oznacza to, że musimy escapować niektóre znaki (mianowicie podwójny cudzysłów "
i backtick `
) w ciągu migawki. W międzyczasie możesz stracić podświetlanie składni dla zawartości migawki (jeśli jest ona w jakimś języku programowania).
Dlatego wprowadziliśmy toMatchFileSnapshot()
, aby jawnie dopasować do pliku. Pozwala to przypisać dowolne rozszerzenie pliku do pliku migawki, co czyni je bardziej czytelnymi.
import { expect, it } from 'vitest';
it('render basic', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./test/basic.output.html');
});
Porówna to z zawartością ./test/basic.output.html
. I może zostać zaktualizowane za pomocą flagi --update
.
Migawki obrazów
Możliwe jest również tworzenie migawek obrazów za pomocą jest-image-snapshot
.
npm i -D jest-image-snapshot
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png')).toMatchImageSnapshot();
});
Niestandardowy serializator
Możesz dodać własną logikę, aby dostosować sposób serializacji migawek. Podobnie jak Jest, Vitest ma domyślne serializatory dla wbudowanych typów JavaScript, elementów HTML, ImmutableJS i elementów React.
Możesz jawnie dodać niestandardowy serializator za pomocą API expect.addSnapshotSerializer
.
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` to funkcja, która serializuje wartość przy użyciu istniejących wtyczek.
return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
});
Obsługujemy również opcję snapshotSerializers do automatycznego dodawania niestandardowych serializatorów.
import { SnapshotSerializer } from 'vitest';
export default {
serialize(val, config, indentation, depth, refs, printer) {
// `printer` to funkcja, która serializuje wartość przy użyciu istniejących wtyczek.
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 'vitest/config';
export default defineConfig({
test: {
snapshotSerializers: ['path/to/custom-serializer.ts'],
},
});
Po dodaniu takiego testu:
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
};
expect(bar).toMatchSnapshot();
});
Otrzymasz następującą migawkę:
Pretty foo: Object {
"x": 1,
"y": 2,
}
Używamy pretty-format
z Jest do serializacji migawek. Więcej na ten temat można przeczytać tutaj: pretty-format.
Różnice w porównaniu do Jest
Vitest zapewnia niemal kompatybilną funkcję migawek z Jest z kilkoma wyjątkami:
1. Nagłówek komentarza w pliku migawki jest inny {#_1-comment-header-in-the-snapshot-file-is-different}
- // Jest Snapshot v1, https://goo.gl/fbAQLP
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
Nie wpływa to na funkcjonalność, ale może wpłynąć na różnice w commitach podczas migracji z Jest.
2. printBasicPrototype
domyślnie ustawione jest na false
{#_2-printbasicprototype-is-default-to-false}
Zarówno migawki Jest, jak i Vitest opierają się na pretty-format
. W Vitest ustawiamy printBasicPrototype
domyślnie na false
, aby zapewnić czystszy wynik migawki, podczas gdy w Jest <29.0.0 jest to domyślnie true
.
import { expect, test } from 'vitest';
test('snapshot', () => {
const bar = [
{
foo: 'bar',
},
];
// w Jest
expect(bar).toMatchInlineSnapshot(`
Array [
Object {
"foo": "bar",
},
]
`);
// w Vitest
expect(bar).toMatchInlineSnapshot(`
[
{
"foo": "bar",
},
]
`);
});
Uważamy, że jest to bardziej rozsądna wartość domyślna dla czytelności i ogólnego komfortu pracy dewelopera (DX). Jeśli nadal preferujesz zachowanie Jest, możesz zmienić swoją konfigurację:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true,
},
},
});
3. Znak >
jest używany jako separator zamiast dwukropka :
dla niestandardowych wiadomości {#_3-chevron-is-used-as-a-separator-instead-of-colon-for-custom-messages}
Vitest używa znaku >
jako separatora zamiast dwukropka :
dla czytelności, gdy podczas tworzenia pliku migawki przekazywana jest niestandardowa wiadomość.
Dla poniższego przykładu kodu testowego:
test('toThrowErrorMatchingSnapshot', () => {
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingSnapshot('hint');
});
W Jest, migawka będzie wyglądać tak:
exports[`toThrowErrorMatchingSnapshot: hint 1`] = `"error"`;
W Vitest, równoważna migawka będzie wyglądać tak:
exports[`toThrowErrorMatchingSnapshot > hint 1`] = `[Error: error]`;
4. Domyślna migawka Error
jest inna dla toThrowErrorMatchingSnapshot
i toThrowErrorMatchingInlineSnapshot
{#_4-default-error-snapshot-is-different-for-tothrowerrormatchingsnapshot-and-tothrowerrormatchinginlinesnapshot}
import { expect, test } from 'vitest'
test('snapshot', () => {
// w Jest i Vitest
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`)
// Jest snapshotuje `Error.message` dla instancji `Error`
// Vitest drukuje tę samą wartość co toMatchInlineSnapshot
expect(() => {
throw new Error('error')
}).toThrowErrorMatchingInlineSnapshot(`"error"`)
}).toThrowErrorMatchingInlineSnapshot(`[Error: error]`)
})