Snapshot
Lernen Sie Snapshots im Video von Vue SchoolSnapshot-Tests sind ein äußerst nützliches Werkzeug, um sicherzustellen, dass sich die Ausgabe Ihrer Funktionen nicht unerwartet ändert.
Bei der Verwendung von Snapshots erstellt Vitest einen Snapshot des angegebenen Wertes und vergleicht diesen anschließend mit einer Referenz-Snapshot-Datei, die neben dem Test gespeichert ist. Der Test schlägt fehl, wenn die beiden Snapshots nicht übereinstimmen. Dies kann entweder bedeuten, dass die Änderung unerwartet ist, oder dass der Referenz-Snapshot an den neuen Zustand des Ergebnisses angepasst werden muss.
Snapshots verwenden
Um einen Wert als Snapshot zu speichern, können Sie toMatchSnapshot()
aus der expect()
API verwenden:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchSnapshot();
});
Wenn dieser Test zum ersten Mal ausgeführt wird, erstellt Vitest eine Snapshot-Datei, die wie folgt aussieht:
// Vitest Snapshot v1, https://www.getbook.com/de/book/vitest-1/guide/snapshot
exports['toUpperCase 1'] = '"FOOBAR"';
Das Snapshot-Artefakt sollte zusammen mit den Codeänderungen committet und im Rahmen Ihres Code-Review-Prozesses überprüft werden. Bei nachfolgenden Testläufen vergleicht Vitest die gerenderte Ausgabe mit dem zuvor gespeicherten Snapshot. Stimmen sie überein, wird der Test bestanden. Stimmen sie nicht überein, hat der Test-Runner entweder einen Fehler in Ihrem Code gefunden, der behoben werden sollte, oder die Implementierung hat sich geändert und der Snapshot muss aktualisiert werden.
WARNING
Wenn Snapshots mit asynchronen, gleichzeitigen Tests verwendet werden, muss expect
aus dem lokalen Test Context genutzt werden, um sicherzustellen, dass der korrekte Test erkannt wird.
Inline-Snapshots
Ähnlich können Sie toMatchInlineSnapshot()
verwenden, um den Snapshot direkt in der Testdatei zu speichern.
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot();
});
Anstatt eine separate Snapshot-Datei zu erstellen, ändert Vitest die Testdatei direkt, um den Snapshot als Zeichenkette zu aktualisieren:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot('"FOOBAR"');
});
Dies ermöglicht es Ihnen, die erwartete Ausgabe direkt zu sehen, ohne zwischen verschiedenen Dateien hin- und herspringen zu müssen.
WARNING
Wenn Snapshots mit asynchronen, gleichzeitigen Tests verwendet werden, muss expect
aus dem lokalen Test Context genutzt werden, um sicherzustellen, dass der korrekte Test erkannt wird.
Snapshots aktualisieren
Wenn der empfangene Wert nicht mit dem Snapshot übereinstimmt, schlägt der Test fehl und zeigt Ihnen den Unterschied zwischen den beiden. Wenn die Snapshot-Änderung erwartet wird, möchten Sie den Snapshot möglicherweise auf den aktuellen Zustand aktualisieren.
Im Watch-Modus können Sie die Taste u
im Terminal drücken, um den fehlgeschlagenen Snapshot direkt zu aktualisieren.
Alternativ können Sie die Option --update
oder -u
in der CLI verwenden, damit Vitest die Snapshots aktualisiert.
vitest -u
Datei-Snapshots
Beim Aufruf von toMatchSnapshot()
speichern wir alle Snapshots in einer formatierten Snapshot-Datei. Dies bedeutet, dass wir bestimmte Zeichen, insbesondere das doppelte Anführungszeichen "
und den Backtick `
, im Snapshot-String escapen müssen. Dabei könnte die Syntaxhervorhebung für den Snapshot-Inhalt verloren gehen (falls dieser in einer bestimmten Programmiersprache vorliegt).
Aus diesem Grund haben wir toMatchFileSnapshot()
eingeführt, um explizit mit einer Datei abzugleichen. Dies ermöglicht es Ihnen, der Snapshot-Datei eine beliebige Dateierweiterung zuzuweisen und die Dateien lesbarer zu gestalten.
import { expect, it } from 'vitest';
it('render basic', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./test/basic.output.html');
});
Der Inhalt wird mit ./test/basic.output.html
verglichen und kann mit der Option --update
zurückgeschrieben werden.
Bild-Snapshots
Es ist auch möglich, Bilder mit jest-image-snapshot
zu snapshotten.
npm i -D jest-image-snapshot
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png')).toMatchImageSnapshot();
});
Benutzerdefinierter Serializer
Sie können Ihre eigene Logik hinzufügen, um die Serialisierung Ihrer Snapshots anzupassen. Wie Jest verfügt Vitest über Standard-Serialisierer für integrierte JavaScript-Typen, HTML-Elemente, ImmutableJS und React-Elemente.
Sie können explizit einen benutzerdefinierten Serializer mithilfe der expect.addSnapshotSerializer
API hinzufügen.
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` ist eine Funktion, die einen Wert unter Verwendung bestehender Plugins serialisiert.
return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
});
Wir unterstützen auch die Einstellung snapshotSerializers, um implizit benutzerdefinierte Serializer hinzuzufügen.
import { SnapshotSerializer } from 'vitest';
export default {
serialize(val, config, indentation, depth, refs, printer) {
// `printer` ist eine Funktion, die einen Wert unter Verwendung bestehender Plugins serialisiert.
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'],
},
});
Nach dem Hinzufügen eines Tests wie diesem:
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
};
expect(bar).toMatchSnapshot();
});
Sie erhalten den folgenden Snapshot:
Pretty foo: Object {
"x": 1,
"y": 2,
}
Wir verwenden Jests pretty-format
zur Serialisierung von Snapshots. Mehr darüber können Sie hier lesen: pretty-format.
Unterschied zu Jest
Vitest bietet eine nahezu kompatible Snapshot-Funktion mit Jests mit einigen Ausnahmen:
1. Der Kommentar-Header in der Snapshot-Datei ist anders {#_1-comment-header-in-the-snapshot-file-is-different}
- // Jest Snapshot v1, https://goo.gl/fbAQLP
+ // Vitest Snapshot v1, https://www.getbook.com/de/book/vitest-1/guide/snapshot
Dies beeinflusst die Funktionalität nicht wesentlich, könnte aber die Änderungen in Ihrem Commit bei der Migration von Jest beeinflussen.
2. printBasicPrototype
ist standardmäßig false
{#_2-printbasicprototype-is-default-to-false}
Sowohl Jests als auch Vitests Snapshots werden von pretty-format
angetrieben. In Vitest setzen wir printBasicPrototype
standardmäßig auf false
, um eine sauberere Snapshot-Ausgabe zu erzielen, während es in Jest <29.0.0 standardmäßig true
ist.
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",
},
]
`);
});
Wir halten dies für einen sinnvolleren Standard hinsichtlich Lesbarkeit und allgemeiner Entwicklererfahrung. Wenn Sie immer noch das Verhalten von Jest bevorzugen, können Sie Ihre Konfiguration ändern:
// vitest.config.js
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true,
},
},
});
3. Chevron >
wird als Trennzeichen anstelle von Doppelpunkt :
für benutzerdefinierte Nachrichten verwendet {#_3-chevron-is-used-as-a-separator-instead-of-colon-for-custom-messages}
Vitest verwendet den Chevron >
als Trennzeichen anstelle des Doppelpunkts :
für bessere Lesbarkeit, wenn beim Erstellen einer Snapshot-Datei eine benutzerdefinierte Nachricht übergeben wird.
Für den folgenden Beispieltestcode:
test('toThrowErrorMatchingSnapshot', () => {
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingSnapshot('hint');
});
In Jest ist der Snapshot:
exports[`toThrowErrorMatchingSnapshot: hint 1`] = `"error"`;
In Vitest ist der äquivalente Snapshot:
exports[`toThrowErrorMatchingSnapshot > hint 1`] = `[Error: error]`;
4. Standard-Error
-Snapshot ist anders für toThrowErrorMatchingSnapshot
und toThrowErrorMatchingInlineSnapshot
{#_4-default-error-snapshot-is-different-for-tothrowerrormatchingsnapshot-and-tothrowerrormatchinginlinesnapshot}
import { expect, test } from 'vitest';
test('snapshot', () => {
// in Jest und Vitest
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`);
// Jest speichert `Error.message` als Snapshot für `Error`-Instanzen
// Vitest gibt denselben Wert wie toMatchInlineSnapshot aus
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingInlineSnapshot(`"error"`);
}).toThrowErrorMatchingInlineSnapshot(`[Error: error]`);
});