Vi
Vitest mette a disposizione funzioni di utilità tramite il suo helper vi
. È accessibile globalmente (quando la configurazione globale è abilitata), oppure puoi importarlo direttamente da vitest
:
import { vi } from 'vitest';
Moduli Mock
Questa sezione descrive l'API che puoi usare quando mocchi un modulo. Tieni presente che Vitest non supporta il mocking di moduli importati con require()
.
vi.mock
- Tipo:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Tipo:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Sostituisce tutti i moduli importati dal percorso fornito con un altro modulo. Puoi usare gli alias Vite configurati all'interno di un percorso. La chiamata a vi.mock
viene sollevata (hoisted), quindi non importa dove la si chiami: verrà sempre eseguita prima di tutte le importazioni. Se hai bisogno di fare riferimento a variabili al di fuori del suo ambito, puoi definirle all'interno di vi.hoisted
e farvi riferimento all'interno di vi.mock
.
WARNING
vi.mock
funziona solo per i moduli che sono stati importati con la parola chiave import
. Non funziona con require
.
Per sollevare vi.mock
, Vitest analizza staticamente i tuoi file. Questo significa che vi
, se non è stato importato direttamente dal pacchetto vitest
(ad esempio, da un file di utilità), non può essere usato. Usa vi.mock
con vi
importato da vitest
, o abilita l'opzione di configurazione globals
.
Vitest non moccherà i moduli che sono stati importati all'interno di un file di setup perché vengono memorizzati nella cache nel momento in cui un file di test è in esecuzione. Puoi chiamare vi.resetModules()
all'interno di vi.hoisted
per cancellare tutte le cache dei moduli prima di eseguire un file di test.
Se la funzione factory
è definita, tutte le importazioni restituiranno il suo risultato. Vitest chiama la factory una sola volta e memorizza nella cache i risultati per tutte le importazioni successive finché non viene chiamato vi.unmock
o vi.doUnmock
.
A differenza di jest
, la factory può essere anche asincrona. Puoi usare vi.importActual
o un helper con la factory passata come primo argomento, per ottenere il modulo originale all'interno.
Puoi anche fornire un oggetto con una proprietà spy
al posto di una funzione factory. Se spy
è true
, Vitest automoccherà il modulo come di consueto, ma non sovrascriverà l'implementazione degli export. Questo è utile se desideri solo verificare che il metodo esportato sia stato chiamato correttamente da un altro metodo.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// chiama l'implementazione originale,
// ma permette di asserire il comportamento in seguito
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Vitest supporta anche una Promise di modulo invece di una stringa nei metodi vi.mock
e vi.doMock
per un migliore supporto IDE. Quando il file viene spostato, il percorso verrà aggiornato e importOriginal
erediterà automaticamente il tipo. L'uso di questa firma imporrà anche che il tipo di ritorno della factory sia compatibile con il modulo originale (mantenendo gli export opzionali).
// @filename: ./path/to/module.js
export declare function total(...numbers: number[]): number;
// @filename: test.js
import { vi } from 'vitest';
// ---cut---
vi.mock(import('./path/to/module.js'), async importOriginal => {
const mod = await importOriginal(); // il tipo è inferito
// ^?
return {
...mod,
// sostituisce alcuni export
total: vi.fn(),
};
});
Sotto il cofano, Vitest opera ancora su una stringa e non su un oggetto modulo.
Se utilizzi TypeScript con alias paths
configurati in tsconfig.json
, tuttavia, il compilatore non sarà in grado di risolvere correttamente i tipi di importazione. Per farlo funzionare, assicurati di sostituire tutte le importazioni con alias con i loro percorsi relativi corrispondenti. Es. usa import('./path/to/module.js')
invece di import('@/module')
.
WARNING
vi.mock
viene sollevato (in altre parole, spostato) in cima al file. Ciò significa che ogni volta che lo si scrive (sia all'interno di beforeEach
o test
), verrà effettivamente chiamato prima di quello.
Ciò significa anche che non è possibile usare variabili all'interno della factory che sono definite al di fuori della factory.
Se hai bisogno di utilizzare variabili all'interno della factory, prova vi.doMock
. Funziona allo stesso modo ma non viene sollevato. Fai attenzione che fa il mock solo delle importazioni successive.
Puoi anche fare riferimento a variabili definite dal metodo vi.hoisted
se è stato dichiarato prima di vi.mock
:
import { namedExport } from './path/to/module.js';
const mocks = vi.hoisted(() => {
return {
namedExport: vi.fn(),
};
});
vi.mock('./path/to/module.js', () => {
return {
namedExport: mocks.namedExport,
};
});
vi.mocked(namedExport).mockReturnValue(100);
expect(namedExport()).toBe(100);
expect(namedExport).toBe(mocks.namedExport);
WARNING
Se stai mocando un modulo con un export predefinito, dovrai fornire una chiave default
all'interno dell'oggetto della funzione factory restituita. Questa è una particolarità specifica dei moduli ES; pertanto, la documentazione di jest
potrebbe differire poiché jest
utilizza moduli CommonJS. Ad esempio,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// ecc...
};
});
Se c'è una cartella __mocks__
accanto a un file di cui stai mocando, e la factory non è fornita, Vitest cercherà di trovare un file con lo stesso nome nella sottocartella __mocks__
e lo userà come modulo effettivo. Se stai mocando una dipendenza, Vitest cercherà una cartella __mocks__
nella root del progetto (il valore predefinito è process.cwd()
). Puoi dire a Vitest dove si trovano le dipendenze tramite l'opzione di configurazione deps.moduleDirectories
.
Ad esempio, hai questa struttura di file:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Se chiami vi.mock
in un file di test senza una factory o opzioni fornite, troverà un file nella cartella __mocks__
da usare come modulo:
import { vi } from 'vitest';
// axios è un export predefinito da `__mocks__/axios.js`
import axios from 'axios';
// increment è un export nominato da `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Tieni presente che se non chiami vi.mock
, i moduli non vengono automaticamente mockati. Per replicare il comportamento di automocking di Jest, puoi chiamare vi.mock
per ogni modulo richiesto all'interno di setupFiles
.
Se non c'è una cartella __mocks__
o una factory fornita, Vitest importerà il modulo originale e automoccherà tutti i suoi export. Per le regole applicate, vedi algoritmo.
vi.doMock
- Tipo:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Tipo:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Uguale a vi.mock
, ma non viene sollevato in cima al file, quindi puoi fare riferimento a variabili nello scope del file globale. La prossima importazione dinamica del modulo verrà mockata.
WARNING
Questo non moccherà i moduli che sono stati importati prima che questo fosse chiamato. Non dimenticare che tutte le importazioni statiche in ESM sono sempre sollevate, quindi posizionare questo prima dell'importazione statica non la forzerà a essere chiamata prima dell'importazione:
vi.doMock('./increment.js'); // questo verrà chiamato _dopo_ l'istruzione import
import { increment } from './increment.js';
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// il modulo non è mockato, perché vi.doMock non è ancora stato chiamato
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// puoi accedere alle variabili all'interno di una factory
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('l\'importazione del modulo successivo importa quello mockato', async () => {
// l'importazione originale NON È STATA MOCKATA, perché vi.doMock viene valutato DOPO le importazioni
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// la nuova importazione dinamica restituisce il modulo mockato
expect(mockedIncrement(1)).toBe(101);
expect(mockedIncrement(1)).toBe(102);
expect(mockedIncrement(1)).toBe(103);
});
vi.mocked
- Tipo:
<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
- Tipo:
<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
Helper di tipo per TypeScript. Restituisce semplicemente l'oggetto che è stato passato.
Quando partial
è true
si aspetterà un Partial<T>
come valore di ritorno. Per impostazione predefinita, questo farà solo credere a TypeScript che i valori di primo livello sono mockati. Puoi passare { deep: true }
come secondo argomento per dire a TypeScript che l'intero oggetto è mockato, se lo è effettivamente.
export function add(x: number, y: number): number {
return x + y;
}
export function fetchSomething(): Promise<Response> {
return fetch('https://vitest.dev/');
}
import * as example from './example';
vi.mock('./example');
test('1 + 1 è uguale a 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('valore di ritorno mockato con tipizzazione solo parzialmente corretta', async () => {
vi.mocked(example.fetchSomething).mockResolvedValue(new Response('hello'));
vi.mocked(example.fetchSomething, { partial: true }).mockResolvedValue({
ok: false,
});
// vi.mocked(example.someFn).mockResolvedValue({ ok: false }) // questo è un errore di tipo
});
vi.importActual
- Tipo:
<T>(path: string) => Promise<T>
Importa il modulo, bypassando tutti i controlli se deve essere mockato. Può essere utile se si desidera mocare il modulo parzialmente.
vi.mock('./example.js', async () => {
const originalModule = await vi.importActual('./example.js');
return { ...originalModule, get: vi.fn() };
});
vi.importMock
- Tipo:
<T>(path: string) => Promise<MaybeMockedDeep<T>>
Importa un modulo con tutte le sue proprietà (incluse le proprietà annidate) mockate. Segue le stesse regole di vi.mock
. Per le regole applicate, vedi algoritmo.
vi.unmock
- Tipo:
(path: string | Promise<Module>) => void
Rimuove il modulo dal registro dei mock. Tutte le chiamate di importazione restituiranno il modulo originale anche se era stato mockato in precedenza. Questa chiamata viene sollevata in cima al file, quindi smoccherà solo i moduli che sono stati definiti in setupFiles
, ad esempio.
vi.doUnmock
- Tipo:
(path: string | Promise<Module>) => void
Uguale a vi.unmock
, ma non viene sollevato in cima al file. La prossima importazione del modulo importerà il modulo originale invece del mock. Questo non smoccherà i moduli importati in precedenza.
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment è già mockato, perché vi.mock è sollevato
increment(1) === 100;
// questo è sollevato, e la factory viene chiamata prima dell'importazione alla riga 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// tutte le chiamate sono mockate, e `increment` restituisce sempre 100
increment(1) === 100;
increment(30) === 100;
// questo non è sollevato, quindi un'altra importazione restituirà un modulo non mockato
vi.doUnmock('./increment.js');
// questo RESTITUISCE ANCORA 100, perché `vi.doUnmock` non rivaluta un modulo
increment(1) === 100;
increment(30) === 100;
// la prossima importazione non è mockata, ora `increment` è la funzione originale che restituisce count + 1
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- Tipo:
() => Vitest
Resetta il registro dei moduli cancellando la cache di tutti i moduli. Ciò consente ai moduli di essere rivalutati quando vengono reimportati. Le importazioni di primo livello non possono essere rivalutate. Potrebbe essere utile per isolare i moduli in cui lo stato locale è in conflitto tra i test.
import { vi } from 'vitest';
import { data } from './data.js'; // Non verrà rivalutato prima di ogni test
beforeEach(() => {
vi.resetModules();
});
test('cambia stato', async () => {
const mod = await import('./some/path.js'); // Verrà rivalutato
mod.changeLocalState('nuovo valore');
expect(mod.getLocalState()).toBe('nuovo valore');
});
test('il modulo ha il vecchio stato', async () => {
const mod = await import('./some/path.js'); // Verrà rivalutato
expect(mod.getLocalState()).toBe('vecchio valore');
});
WARNING
Non resetta il registro dei mock. Per cancellare il registro dei mock, usa vi.unmock
o vi.doUnmock
.
vi.dynamicImportSettled
Attende il caricamento di tutte le importazioni dinamiche. Utile se si ha una chiamata sincrona che avvia l'importazione di un modulo che altrimenti non si potrebbe attendere.
import { expect, test } from 'vitest';
// non è possibile tracciare l'importazione perché la Promise non viene restituita
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('le operazioni sono risolte', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
Se durante un'importazione dinamica viene avviata un'altra importazione dinamica, questo metodo attenderà che tutte siano risolte.
Questo metodo attenderà anche il prossimo tick di setTimeout
dopo che l'importazione è stata risolta, in modo che tutte le operazioni sincrone siano completate al momento della sua risoluzione.
Mocking di Funzioni e Oggetti
Questa sezione descrive come lavorare con i mock di metodi e sostituire le variabili ambientali e globali.
vi.fn
- Tipo:
(fn?: Function) => Mock
Crea una spia su una funzione, sebbene possa essere avviata senza una. Ogni volta che una funzione viene invocata, memorizza i suoi argomenti di chiamata, i valori di ritorno e le istanze. Inoltre, è possibile manipolare il suo comportamento con i metodi. Se non viene fornita alcuna funzione, il mock restituirà undefined
quando invocato.
const getApples = vi.fn(() => 0);
getApples();
expect(getApples).toHaveBeenCalled();
expect(getApples).toHaveReturnedWith(0);
getApples.mockReturnValueOnce(5);
const res = getApples();
expect(res).toBe(5);
expect(getApples).toHaveNthReturnedWith(2, 5);
vi.mockObject 3.2.0+
- Tipo:
<T>(value: T) => MaybeMockedDeep<T>
Mocca profondamente le proprietà e i metodi di un dato oggetto nello stesso modo in cui vi.mock()
mocca gli export dei moduli. Per i dettagli, consultare automocking.
const original = {
simple: () => 'value',
nested: {
method: () => 'real',
},
prop: 'foo',
};
const mocked = vi.mockObject(original);
expect(mocked.simple()).toBe(undefined);
expect(mocked.nested.method()).toBe(undefined);
expect(mocked.prop).toBe('foo');
mocked.simple.mockReturnValue('mocked');
mocked.nested.method.mockReturnValue('mocked nested');
expect(mocked.simple()).toBe('mocked');
expect(mocked.nested.method()).toBe('mocked nested');
vi.isMockFunction
- Tipo:
(fn: Function) => boolean
Verifica che un dato parametro sia una funzione mock. Se stai usando TypeScript, restringerà anche il suo tipo.
vi.clearAllMocks
Chiama .mockClear()
su tutte le spie. Questo cancellerà la cronologia dei mock senza influenzare le implementazioni dei mock.
vi.resetAllMocks
Chiama .mockReset()
su tutte le spie. Questo cancellerà la cronologia dei mock e resetterà l'implementazione di ogni mock alla sua originale.
vi.restoreAllMocks
Chiama .mockRestore()
su tutte le spie. Questo cancellerà la cronologia dei mock, ripristinerà tutte le implementazioni originali dei mock e ripristinerà i descrittori originali degli oggetti spiati.
vi.spyOn
- Tipo:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Crea una spia su un metodo o un getter/setter di un oggetto, simile a vi.fn()
. Restituisce una funzione mock.
let apples = 0;
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockImplementation(() => apples);
apples = 1;
expect(cart.getApples()).toBe(1);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveReturnedWith(1);
TIP
Negli ambienti che supportano la Gestione Esplicita delle Risorse, puoi usare using
invece di const
per chiamare automaticamente mockRestore
su qualsiasi funzione mockata quando il blocco contenitore viene chiuso. Questo è particolarmente utile per i metodi spiati:
it('chiama console.log', () => {
using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
debug('messaggio')
expect(spy).toHaveBeenCalled()
})
// console.log viene ripristinato qui
TIP
Puoi chiamare vi.restoreAllMocks
all'interno di afterEach
(o abilitare test.restoreMocks
) per ripristinare tutti i metodi alle loro implementazioni originali. Questo ripristinerà il descrittore dell'oggetto originale, quindi non sarà possibile modificare l'implementazione del metodo:
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10);
console.log(cart.getApples()); // 10
vi.restoreAllMocks();
console.log(cart.getApples()); // 42
spy.mockReturnValue(10);
console.log(cart.getApples()); // ancora 42!
TIP
Non è possibile spiare i metodi esportati in Modalità Browser. Invece, puoi spiare ogni metodo esportato chiamando vi.mock("./file-path.js", { spy: true })
. Questo moccherà ogni export ma manterrà la sua implementazione intatta, permettendoti di asserire se il metodo è stato chiamato correttamente.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
calculator(1, 2);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
E sebbene sia possibile spiare gli export in jsdom
o altri ambienti Node.js, questo potrebbe cambiare in futuro.
vi.stubEnv
- Tipo:
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
Modifica il valore della variabile d'ambiente su process.env
e import.meta.env
. Puoi ripristinare il suo valore chiamando vi.unstubAllEnvs
.
import { vi } from 'vitest';
// `process.env.NODE_ENV` e `import.meta.env.NODE_ENV`
// sono "development" prima di chiamare "vi.stubEnv"
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
vi.stubEnv('NODE_ENV', undefined);
process.env.NODE_ENV === undefined;
import.meta.env.NODE_ENV === undefined;
// non cambia altri env
import.meta.env.MODE === 'development';
TIP
Puoi anche cambiare il valore semplicemente assegnandolo, ma non sarai in grado di usare vi.unstubAllEnvs
per ripristinare il valore precedente:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- Tipo:
() => Vitest
Ripristina tutti i valori di import.meta.env
e process.env
che sono stati modificati con vi.stubEnv
. Quando viene chiamato per la prima volta, Vitest ricorda il valore originale e lo memorizzerà, finché unstubAllEnvs
non verrà chiamato di nuovo.
import { vi } from 'vitest';
// `process.env.NODE_ENV` e `import.meta.env.NODE_ENV`
// sono "development" prima di chiamare stubEnv
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
vi.stubEnv('NODE_ENV', 'staging');
process.env.NODE_ENV === 'staging';
import.meta.env.NODE_ENV === 'staging';
vi.unstubAllEnvs();
// ripristina al valore che era stato memorizzato prima della prima chiamata "stubEnv"
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Tipo:
(name: string | number | symbol, value: unknown) => Vitest
Modifica il valore di una variabile globale. Puoi ripristinare il suo valore originale chiamando vi.unstubAllGlobals
.
import { vi } from 'vitest';
// `innerWidth` è "0" prima di chiamare stubGlobal
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// se stai usando jsdom o happy-dom
window.innerWidth === 100;
TIP
Puoi anche cambiare il valore semplicemente assegnandolo a globalThis
o window
(se stai usando l'ambiente jsdom
o happy-dom
), ma non sarai in grado di usare vi.unstubAllGlobals
per ripristinare il valore originale:
globalThis.innerWidth = 100;
// se stai usando jsdom o happy-dom
window.innerWidth = 100;
vi.unstubAllGlobals
- Tipo:
() => Vitest
Ripristina tutti i valori globali su globalThis
/global
(e window
/top
/self
/parent
, se stai usando l'ambiente jsdom
o happy-dom
) che sono stati modificati con vi.stubGlobal
. Quando viene chiamato per la prima volta, Vitest ricorda il valore originale e lo memorizzerà, finché unstubAllGlobals
non verrà chiamato di nuovo.
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver è "undefined" prima di chiamare "stubGlobal"
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// se stai usando jsdom o happy-dom
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// lancia ReferenceError, perché non è definito
IntersectionObserver === undefined;
Timer Fittizi
Questa sezione descrive come lavorare con i timer fittizi.
vi.advanceTimersByTime
- Tipo:
(ms: number) => Vitest
Questo metodo invocherà ogni timer avviato finché non sarà trascorso il numero specificato di millisecondi o la coda sarà vuota - a seconda di quale condizione si verifichi per prima.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- Tipo:
(ms: number) => Promise<Vitest>
Questo metodo invocherà ogni timer avviato finché non sarà trascorso il numero specificato di millisecondi o la coda sarà vuota - a seconda di quale condizione si verifichi per prima. Questo includerà i timer impostati in modo asincrono.
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersByTimeAsync(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- Tipo:
() => Vitest
Chiamerà il prossimo timer disponibile. Utile per effettuare asserzioni tra ogni chiamata del timer. Puoi concatenare le chiamate per gestire i timer autonomamente.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Tipo:
() => Promise<Vitest>
Chiamerà il prossimo timer disponibile e attenderà che sia risolto se è stato impostato in modo asincrono. Utile per effettuare asserzioni tra ogni chiamata del timer.
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersToNextTimerAsync(); // log: 1
expect(console.log).toHaveBeenCalledWith(1);
await vi.advanceTimersToNextTimerAsync(); // log: 2
await vi.advanceTimersToNextTimerAsync(); // log: 3
vi.advanceTimersToNextFrame 2.1.0+
- Tipo:
() => Vitest
Simile a vi.advanceTimersByTime
, ma farà avanzare i timer dei millisecondi necessari per eseguire i callback attualmente programmati con requestAnimationFrame
.
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- Tipo:
() => number
Ottiene il numero di timer in attesa.
vi.clearAllTimers
Rimuove tutti i timer programmati per l'esecuzione. Questi timer non verranno mai eseguiti in futuro.
vi.getMockedSystemTime
- Tipo:
() => Date | null
Restituisce la data corrente moccata. Se la data non è moccata, il metodo restituirà null
.
vi.getRealSystemTime
- Tipo:
() => number
Quando si usa vi.useFakeTimers
, le chiamate a Date.now
vengono mockate. Se è necessario ottenere il tempo reale in millisecondi, è possibile chiamare questa funzione.
vi.runAllTicks
- Tipo:
() => Vitest
Chiama ogni microtask che è stato messo in coda da process.nextTick
. Questo eseguirà anche tutte le microtask programmate autonomamente.
vi.runAllTimers
- Tipo:
() => Vitest
Questo metodo invocherà ogni timer avviato finché la coda dei timer non sarà vuota. Ciò significa che ogni timer chiamato durante runAllTimers
verrà attivato. Se si ha un intervallo infinito, lancerà un'eccezione dopo 10.000 tentativi (configurabile con fakeTimers.loopLimit
).
let i = 0;
setTimeout(() => console.log(++i));
const interval = setInterval(() => {
console.log(++i);
if (i === 3) {
clearInterval(interval);
}
}, 50);
vi.runAllTimers();
// log: 1
// log: 2
// log: 3
vi.runAllTimersAsync
- Tipo:
() => Promise<Vitest>
Questo metodo invocherà in modo asincrono ogni timer avviato finché la coda dei timer non sarà vuota. Ciò significa che ogni timer chiamato durante runAllTimersAsync
verrà attivato, inclusi i timer asincroni. Se si ha un intervallo infinito, lancerà un'eccezione dopo 10.000 tentativi (configurabile con fakeTimers.loopLimit
).
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- Tipo:
() => Vitest
Questo metodo chiamerà ogni timer che è stato avviato dopo la chiamata a vi.useFakeTimers
. Non attiverà alcun timer che è stato avviato durante la sua chiamata.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Tipo:
() => Promise<Vitest>
Questo metodo chiamerà in modo asincrono ogni timer che è stato avviato dopo la chiamata a vi.useFakeTimers
, inclusi quelli asincroni. Non attiverà alcun timer che è stato avviato durante la sua chiamata.
setTimeout(() => {
console.log(1);
}, 100);
setTimeout(() => {
Promise.resolve().then(() => {
console.log(2);
setInterval(() => {
console.log(3);
}, 40);
});
}, 10);
await vi.runOnlyPendingTimersAsync();
// log: 2
// log: 3
// log: 3
// log: 1
vi.setSystemTime
- Tipo:
(date: string | number | Date) => void
Se i timer fittizi sono abilitati, questo metodo simula un utente che cambia l'orologio di sistema (influirà sulle API relative alla data come hrtime
, performance.now
o new Date()
) - tuttavia, non attiverà alcun timer. Se i timer fittizi non sono abilitati, questo metodo moccherà solo le chiamate Date.*
.
Utile se è necessario testare qualcosa che dipende dalla data corrente - ad esempio le chiamate Luxon all'interno del proprio codice.
Accetta gli stessi argomenti stringa e numerici di Date
.
const date = new Date(1998, 11, 19);
vi.useFakeTimers();
vi.setSystemTime(date);
expect(Date.now()).toBe(date.valueOf());
vi.useRealTimers();
vi.useFakeTimers
- Tipo:
(config?: FakeTimerInstallOpts) => Vitest
Per abilitare il mocking dei timer, è necessario chiamare questo metodo. Avvolgerà tutte le chiamate successive ai timer (come setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
e Date
) finché non verrà chiamato vi.useRealTimers()
.
Il mocking di nextTick
non è supportato quando si esegue Vitest all'interno di node:child_process
usando --pool=forks
. NodeJS usa process.nextTick
internamente in node:child_process
e si blocca quando viene mockato. Il mocking di nextTick
è supportato quando si esegue Vitest con --pool=threads
.
L'implementazione si basa internamente su @sinonjs/fake-timers
.
TIP
vi.useFakeTimers()
non mocca automaticamente process.nextTick
e queueMicrotask
. Ma è possibile abilitarlo specificando l'opzione nell'argomento toFake
: vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })
.
vi.isFakeTimers
- Tipo:
() => boolean
Restituisce true
se i timer fittizi sono abilitati.
vi.useRealTimers
- Tipo:
() => Vitest
Quando i timer sono scaduti, puoi chiamare questo metodo per ripristinare i timer mockati alle loro implementazioni originali. Tutti i timer che erano stati programmati in precedenza verranno scartati.
Varie
Un insieme di utili funzioni helper fornite da Vitest.
vi.waitFor
- Tipo:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Attende che il callback venga eseguito con successo. Se il callback lancia un errore o restituisce una Promise rifiutata, continuerà ad attendere finché non avrà successo o scadrà il tempo.
Se le opzioni sono impostate su un numero, l'effetto è equivalente a impostare { timeout: options }
.
Questo è molto utile quando è necessario attendere il completamento di un'azione asincrona, ad esempio, quando si avvia un server e si deve attendere che si avvii.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Server avviato con successo', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('Server non avviato');
}
console.log('Server avviato');
},
{
timeout: 500, // il valore predefinito è 1000
interval: 20, // il valore predefinito è 50
}
);
expect(server.isReady).toBe(true);
});
Funziona anche per i callback asincroni
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('L\'elemento esiste nel DOM', async () => {
// inizia a popolare il DOM
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// prova a ottenere l'elemento finché non esiste
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // il valore predefinito è 1000
interval: 20, // il valore predefinito è 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Se viene usato vi.useFakeTimers
, vi.waitFor
chiama automaticamente vi.advanceTimersByTime(interval)
in ogni callback di controllo.
vi.waitUntil
- Tipo:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Questo è simile a vi.waitFor
, ma se il callback lancia errori, l'esecuzione viene immediatamente interrotta e viene ricevuto un messaggio di errore. Se il callback restituisce un valore falsy, il controllo successivo continuerà finché non verrà restituito un valore truthy. Questo è utile quando è necessario attendere che qualcosa esista prima di fare il passo successivo.
Si veda l'esempio seguente. Possiamo usare vi.waitUntil
per attendere che l'elemento appaia sulla pagina, e poi possiamo fare qualcosa con l'elemento.
import { expect, test, vi } from 'vitest';
test('Elemento renderizzato correttamente', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // il valore predefinito è 1000
interval: 20, // il valore predefinito è 50
});
// fai qualcosa con l'elemento
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- Tipo:
<T>(factory: () => T) => T
Tutte le istruzioni import
statiche nei moduli ES vengono sollevate in cima al file, quindi qualsiasi codice definito prima delle importazioni verrà effettivamente eseguito dopo che le importazioni sono state valutate.
Tuttavia, può essere utile invocare alcuni effetti collaterali come il mocking delle date prima di importare un modulo.
Per aggirare questa limitazione, è possibile riscrivere le importazioni statiche in quelle dinamiche in questo modo:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
Quando si esegue vitest
, è possibile farlo automaticamente usando il metodo vi.hoisted
. Sotto il cofano, Vitest convertirà le importazioni statiche in quelle dinamiche con live-binding preservati.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
LE IMPORTAZIONI NON SONO DISPONIBILI
L'esecuzione del codice prima delle importazioni significa che non è possibile accedere alle variabili importate perché non sono ancora definite:
import { value } from './some/module.js';
vi.hoisted(() => { value }); // lancia un errore
Questo codice produrrà un errore:
Impossibile accedere a '__vi_import_0__' prima dell'inizializzazione
Se è necessario accedere a una variabile da un altro modulo all'interno di vi.hoisted
, usare l'importazione dinamica:
await vi.hoisted(async () => {
const { value } = await import('./some/module.js');
});
Tuttavia, è sconsigliato importare qualsiasi cosa all'interno di vi.hoisted
perché le importazioni sono già sollevate (hoisted) - se è necessario eseguire qualcosa prima che i test siano in esecuzione, eseguirlo nel modulo importato stesso.
Questo metodo restituisce il valore che è stato restituito dalla factory. Puoi usare quel valore nelle tue factory vi.mock
se hai bisogno di un facile accesso a variabili definite localmente:
import { expect, vi } from 'vitest';
import { originalMethod } from './path/to/module.js';
const { mockedMethod } = vi.hoisted(() => {
return { mockedMethod: vi.fn() };
});
vi.mock('./path/to/module.js', () => {
return { originalMethod: mockedMethod };
});
mockedMethod.mockReturnValue(100);
expect(originalMethod()).toBe(100);
Si noti che questo metodo può anche essere chiamato in modo asincrono anche se il proprio ambiente non supporta l'await di primo livello:
const json = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- Tipo:
RuntimeConfig
Aggiorna la configurazione per il file di test corrente. Questo metodo supporta solo le opzioni di configurazione che influenzeranno il file di test corrente:
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// supporta l'intero oggetto
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// supporta solo "sequence.hooks"
},
});
vi.resetConfig
- Tipo:
RuntimeConfig
Se vi.setConfig
è stato chiamato in precedenza, questo ripristinerà la configurazione allo stato originale.