Snímky
Naučte se pracovat se snímky prostřednictvím videa z Vue SchoolSnímkové testy jsou velmi užitečný nástroj, který vám pomůže zajistit, že se výstup vašich funkcí nezmění neočekávaným způsobem.
Při použití snímků Vitest vytvoří snímek dané hodnoty a porovná jej s referenčním snímkem uloženým v souboru vedle testu. Pokud se snímky neshodují, test selže. To může znamenat buď neočekávanou změnu, nebo že je potřeba aktualizovat referenční snímek na novou verzi výsledku.
Použití snímků
Pro vytvoření snímku hodnoty použijte metodu toMatchSnapshot()
z API expect()
:
function toUpperCase(str: string) {
return str;
}
// ---cut---
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchSnapshot();
});
Při prvním spuštění tohoto testu Vitest vytvoří soubor se snímkem, který bude vypadat takto:
// Vitest Snapshot v1, https://www.getbook.com/cs/book/vitest-1/guide/snapshot
exports['toUpperCase 1'] = '"FOOBAR"';
Soubor se snímkem by měl být uložen spolu se změnami kódu a zkontrolován v rámci revize kódu. Při následných spuštěních testů Vitest porovná aktuální výstup s uloženým snímkem. Pokud se hodnoty shodují, test projde. Pokud se hodnoty neshodují, testovací nástroj buď našel chybu ve vašem kódu, kterou je třeba opravit, nebo se implementace změnila a je třeba aktualizovat snímek.
WARNING
Při použití snímků v asynchronních testech, které běží souběžně, je nutné použít expect
z lokálního Testovacího kontextu, aby se zajistilo správné fungování testu.
Inline snímky
Podobně můžete použít metodu toMatchInlineSnapshot()
k uložení snímku přímo v testovacím souboru.
function toUpperCase(str: string) {
return str;
}
// ---cut---
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot();
});
Místo vytvoření samostatného souboru se snímkem Vitest přímo upraví testovací soubor a aktualizuje snímek jako řetězec:
function toUpperCase(str: string) {
return str;
}
// ---cut---
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot('"FOOBAR"');
});
Díky tomu uvidíte očekávaný výstup přímo v kódu, bez nutnosti přepínání mezi soubory.
WARNING
Při použití snímků v asynchronních testech, které běží souběžně, je nutné použít expect
z lokálního Testovacího kontextu, aby se zajistilo správné fungování testu.
Aktualizace snímků
Pokud se získaná hodnota neshoduje se snímkem, test selže a zobrazí se rozdíl mezi nimi. Pokud je změna snímku očekávaná, můžete snímek aktualizovat na aktuální stav.
V režimu watch můžete stisknout klávesu u
v terminálu a přímo aktualizovat neúspěšné snímky.
Nebo můžete použít přepínač --update
nebo -u
v CLI, aby Vitest aktualizoval snímky.
vitest -u
Souborové snímky
Při volání toMatchSnapshot()
se všechny snímky ukládají do formátovaného souboru se snímky. To vyžaduje escapování některých znaků v řetězci snímku, konkrétně dvojitých uvozovek "
a zpětných apostrofů ```. Navíc můžete ztratit zvýraznění syntaxe pro obsah snímku (pokud je v nějakém jazyce).
Pro tento účel zavádíme metodu toMatchFileSnapshot()
, která umožňuje ukládat snímky do samostatných souborů. To vám umožní přiřadit souboru se snímkem libovolnou příponu a učinit je čitelnějšími.
import { expect, it } from 'vitest';
it('render basic', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./test/basic.output.html');
});
Test bude porovnávat obsah se souborem ./test/basic.output.html
. Soubor lze aktualizovat pomocí příznaku --update
.
Obrázkové snímky
Snímky obrázků lze také vytvářet pomocí knihovny jest-image-snapshot
.
npm i -D jest-image-snapshot
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png')).toMatchImageSnapshot();
});
Další informace naleznete v příkladu examples/image-snapshot
.
Vlastní způsob serializace
Můžete přidat vlastní logiku pro úpravu způsobu, jakým jsou vaše snímky serializovány. Podobně jako Jest má i Vitest výchozí serializátory pro základní typy JavaScriptu, prvky HTML, ImmutableJS a pro prvky React.
Můžete explicitně přidat vlastní serializátor pomocí API expect.addSnapshotSerializer
.
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` je funkce, která serializuje hodnotu pomocí stávajících pluginů.
return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
});
Rovněž podporujeme možnost snapshotSerializers
pro implicitní přidání vlastních serializátorů.
import { SnapshotSerializer } from 'vitest';
export default {
serialize(val, config, indentation, depth, refs, printer) {
// `printer` je funkce, která serializuje hodnotu pomocí stávajících pluginů.
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'],
},
});
Po přidání testu, jako je tento:
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
};
expect(bar).toMatchSnapshot();
});
Výsledný snímek bude vypadat takto:
Pretty foo: Object {
"x": 1,
"y": 2,
}
Pro serializaci snímků Vitest používá knihovnu pretty-format
od Jestu. Více si o tom můžete přečíst zde: pretty-format.
Rozdíl od Jest
Vitest poskytuje téměř kompatibilní funkci snímků s Jest's s několika výjimkami:
1. Hlavička komentáře v souboru snímku je odlišná {#_1-comment-header-in-the-snapshot-file-is-different}
- // Jest Snapshot v1, https://goo.gl/fbAQLP
+ // Vitest Snapshot v1, https://www.getbook.com/cs/book/vitest-1/guide/snapshot
Tato změna neovlivňuje funkčnost, ale může ovlivnit rozdíly v commitech při migraci z Jest.
2. printBasicPrototype
je ve výchozím nastavení false
{#_2-printbasicprototype-is-default-to-false}
Snímky v Jestu i Vitestu využívají pretty-format
. Ve Vitest nastavujeme printBasicPrototype
ve výchozím nastavení na false
, abychom poskytli čistší výstup snímku, zatímco v Jest <29.0.0 je ve výchozím nastavení true
.
import { expect, test } from 'vitest';
test('snapshot', () => {
const bar = [
{
foo: 'bar',
},
];
// in Jest
expect(bar).toMatchInlineSnapshot(`
Array [
Object {
"foo": "bar",
},
]
`);
// in Vitest
expect(bar).toMatchInlineSnapshot(`
[
{
"foo": "bar",
},
]
`);
});
Toto výchozí nastavení považujeme za vhodnější pro čitelnost a celkový DX (Developer Experience). Pokud stále preferujete chování Jest, můžete změnit konfiguraci:
// vitest.config.js
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true,
},
},
});
3. Chevron >
se používá jako oddělovač namísto dvojtečky :
pro vlastní zprávy {#_3-chevron-is-used-as-a-separator-instead-of-colon-for-custom-messages}
Vitest používá chevron >
jako oddělovač namísto dvojtečky :
pro lepší čitelnost, když se při vytváření snímku předává vlastní zpráva.
Pro následující příklad testovacího kódu:
test('toThrowErrorMatchingSnapshot', () => {
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingSnapshot('hint');
});
V Jest bude snímek:
exports[`toThrowErrorMatchingSnapshot: hint 1`] = `"error"`;
Ve Vitestu bude ekvivalentní snapshot vypadat takto:
exports[`toThrowErrorMatchingSnapshot > hint 1`] = `[Error: error]`;
4. Výchozí snapshot chyby Error
se liší pro toThrowErrorMatchingSnapshot
a toThrowErrorMatchingInlineSnapshot
{#_4-default-error-snapshot-is-different-for-tothrowerrormatchingsnapshot-and-tothrowerrormatchinginlinesnapshot}
import { expect, test } from 'vitest';
// ---cut---
test('snapshot', () => {
//
// in Jest
//
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`);
// Jest vytváří snapshot `Error.message` pro instance `Error`
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingInlineSnapshot(`"error"`);
//
// in Vitest
//
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`);
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingInlineSnapshot(`[Error: error]`);
});