Snapshot
Impara gli Snapshot tramite video da Vue SchoolI test snapshot sono uno strumento estremamente utile ogni volta che si desidera assicurarsi che l'output delle proprie funzioni non cambi inaspettatamente.
Quando si utilizzano gli snapshot, Vitest acquisisce uno snapshot del valore fornito e lo confronta con un file snapshot di riferimento memorizzato insieme al test. Il test fallirà se i due snapshot non corrispondono: ciò indica che il cambiamento è inatteso oppure che lo snapshot di riferimento deve essere aggiornato con la nuova versione del risultato.
Utilizzo degli Snapshot
Per creare uno snapshot di un valore, è possibile utilizzare toMatchSnapshot()
dall'API expect()
:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchSnapshot();
});
La prima volta che questo test viene eseguito, Vitest crea un file snapshot simile a questo:
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
exports['toUpperCase 1'] = '"FOOBAR"';
L'artefatto snapshot dovrebbe essere committato insieme alle modifiche al codice e revisionato come parte del processo di revisione del codice. Nelle esecuzioni successive dei test, Vitest confronterà l'output generato con lo snapshot precedente. Se corrispondono, il test passerà. Se non corrispondono, significa che il test runner ha rilevato un bug nel codice che dovrebbe essere corretto, oppure che l'implementazione è cambiata e lo snapshot deve essere aggiornato.
WARNING
Quando si utilizzano gli Snapshot con test asincroni concorrenti, è necessario usare expect
dal Contesto di Test locale per garantire che il test corretto sia identificato.
Snapshot Inline
Allo stesso modo, è possibile utilizzare toMatchInlineSnapshot()
per memorizzare lo snapshot direttamente all'interno del file di test.
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot();
});
Invece di creare un file snapshot separato, Vitest modificherà direttamente il file di test per aggiornare lo snapshot come stringa:
import { expect, it } from 'vitest';
it('toUpperCase', () => {
const result = toUpperCase('foobar');
expect(result).toMatchInlineSnapshot('"FOOBAR"');
});
Questo permette di visualizzare l'output atteso direttamente senza dover passare da un file all'altro.
WARNING
Quando si utilizzano gli Snapshot con test asincroni concorrenti, è necessario usare expect
dal Contesto di Test locale per garantire che il test corretto sia identificato.
Aggiornamento degli Snapshot
Quando il valore ricevuto non corrisponde allo snapshot, il test fallisce e mostra la differenza tra i due. Se il cambiamento dello snapshot è previsto, potrebbe essere necessario aggiornare lo snapshot.
In modalità watch, premi il tasto u
nel terminale per aggiornare direttamente lo snapshot fallito.
In alternativa, puoi usare il flag --update
o -u
nella CLI per fare in modo che Vitest aggiorni gli snapshot.
vitest -u
Snapshot di File
Quando si chiama toMatchSnapshot()
, tutti gli snapshot vengono memorizzati in un file snapshot formattato. Questo implica la necessità di eseguire l'escape di alcuni caratteri, come le virgolette doppie "
e l'apice inverso `
, all'interno della stringa dello snapshot. Inoltre, si potrebbe perdere l'evidenziazione della sintassi per il contenuto dello snapshot (se si tratta di codice in una lingua specifica).
In considerazione di ciò, abbiamo introdotto toMatchFileSnapshot()
per confrontare esplicitamente con un file. Questo consente di assegnare qualsiasi estensione di file allo snapshot e di renderlo più leggibile.
import { expect, it } from 'vitest';
it('render basic', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./test/basic.output.html');
});
Questo confronterà il risultato con il contenuto di ./test/basic.output.html
. Inoltre, può essere aggiornato con il flag --update
.
Snapshot di Immagini
È anche possibile creare snapshot di immagini utilizzando jest-image-snapshot
.
npm i -D jest-image-snapshot
test('image snapshot', () => {
expect(readFileSync('./test/stubs/input-image.png')).toMatchImageSnapshot();
});
Serializzatore Personalizzato
È possibile aggiungere una logica personalizzata per modificare il modo in cui i propri snapshot vengono serializzati. Come Jest, Vitest dispone di serializzatori predefiniti per i tipi JavaScript incorporati, gli elementi HTML, ImmutableJS e gli elementi React.
È possibile aggiungere esplicitamente un serializzatore personalizzato utilizzando l'API expect.addSnapshotSerializer
.
expect.addSnapshotSerializer({
serialize(val, config, indentation, depth, refs, printer) {
// `printer` è una funzione che serializza un valore utilizzando i plugin esistenti.
return `Pretty foo: ${printer(val.foo, config, indentation, depth, refs)}`;
},
test(val) {
return val && Object.prototype.hasOwnProperty.call(val, 'foo');
},
});
Supportiamo anche l'opzione snapshotSerializers per aggiungere serializzatori personalizzati in modo implicito.
import { SnapshotSerializer } from 'vitest';
export default {
serialize(val, config, indentation, depth, refs, printer) {
// `printer` è una funzione che serializza un valore utilizzando i plugin esistenti.
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'],
},
});
Dopo aver aggiunto un test simile a questo:
test('foo snapshot test', () => {
const bar = {
foo: {
x: 1,
y: 2,
},
};
expect(bar).toMatchSnapshot();
});
Si otterrà il seguente snapshot:
Pretty foo: Object {
"x": 1,
"y": 2,
}
Utilizziamo pretty-format
di Jest per serializzare gli snapshot. Puoi leggere di più qui: pretty-format.
Differenze da Jest
Vitest offre una funzionalità Snapshot quasi compatibile con quella di Jest con alcune eccezioni significative:
1. L'intestazione del commento nel file snapshot è diversa {#_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
Questo non influisce sulla funzionalità, ma potrebbe influenzare il diff del tuo commit durante la migrazione da Jest.
2. printBasicPrototype
è predefinito su false
{#_2-printbasicprototype-is-default-to-false}
Gli snapshot di Jest e Vitest sono entrambi alimentati da pretty-format
. In Vitest abbiamo impostato printBasicPrototype
su false
di default per fornire un output snapshot più pulito, mentre in Jest <29.0.0 è 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",
},
]
`);
});
Riteniamo che questo sia un valore predefinito più ragionevole per la leggibilità e l'esperienza complessiva di sviluppo. Se preferisci ancora il comportamento di Jest, puoi modificare la tua configurazione:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
snapshotFormat: {
printBasicPrototype: true,
},
},
});
3. Il simbolo di maggiore >
è usato come separatore invece dei due punti :
per i messaggi personalizzati {#_3-chevron-is-used-as-a-separator-instead-of-colon-for-custom-messages}
Vitest utilizza il simbolo di maggiore >
come separatore invece dei due punti :
per migliorare la leggibilità, quando si passa un messaggio personalizzato durante la creazione di un file snapshot.
Per il seguente codice di test di esempio:
test('toThrowErrorMatchingSnapshot', () => {
expect(() => {
throw new Error('error');
}).toThrowErrorMatchingSnapshot('hint');
});
In Jest, lo snapshot sarà:
exports[`toThrowErrorMatchingSnapshot: hint 1`] = `"error"`;
In Vitest, lo snapshot corrispondente sarà:
exports[`toThrowErrorMatchingSnapshot > hint 1`] = `[Error: error]`;
4. Lo snapshot Error
predefinito è diverso per toThrowErrorMatchingSnapshot
e toThrowErrorMatchingInlineSnapshot
{#_4-default-error-snapshot-is-different-for-tothrowerrormatchingsnapshot-and-tothrowerrormatchinginlinesnapshot}
import { expect, test } from 'vitest'
test('snapshot', () => {
// in Jest e Vitest
expect(new Error('error')).toMatchInlineSnapshot(`[Error: error]`)
// Jest acquisisce lo snapshot di `Error.message` per le istanze di `Error`
// Vitest produce lo stesso valore di toMatchInlineSnapshot
expect(() => {
throw new Error('error')
}).toThrowErrorMatchingInlineSnapshot(`"error"`)
}).toThrowErrorMatchingInlineSnapshot(`[Error: error]`)
})