vi
Vitest fornisce funzioni di utilità per aiutarti con il suo helper vi
. Puoi accedervi a livello globale (quando la configurazione globals è abilitata), o importarlo direttamente da vitest
:
import { vi } from 'vitest';
Mock di Moduli
Questa sezione descrive l'API che puoi utilizzare quando mocki un modulo. Attenzione: 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 di Vite configurati all'interno di un path. La chiamata a vi.mock
viene sollevata (hoisted), quindi non importa dove la 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 (hoist) vi.mock
, Vitest analizza staticamente i tuoi file. Ciò significa che se vi
non è stato importato direttamente dal pacchetto vitest
(ad esempio, da un file di utilità) non può essere utilizzato. Usa vi.mock
con vi
importato da vitest
, o abilita l'opzione di configurazione globals
.
Vitest non mocka 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 solo una volta e memorizza nella cache i risultati per tutte le importazioni successive fino a quando non viene chiamato vi.unmock
o vi.doUnmock
.
A differenza di jest
, la factory può essere asincrona. Puoi usare vi.importActual
o un helper con la factory passata come primo argomento, e ottenere il modulo originale all'interno.
A partire da Vitest 2.1, puoi anche fornire un oggetto con una proprietà spy
invece di una funzione factory. Se spy
è true
, Vitest automockera il modulo come al solito, ma non sovrascriverà l'implementazione degli export. Questo è utile se vuoi solo asserire che il metodo esportato è 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 consente 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 path verrà aggiornato e importOriginal
erediterà automaticamente il tipo. L'utilizzo 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,
// sostituisci alcuni export
total: vi.fn(),
};
});
Internamente, Vitest opera ancora su una stringa e non su un oggetto modulo.
Se stai usando 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 corrispondenti path relativi. Ad esempio, 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 scrivi (sia all'interno di beforeEach
o test
), verrà effettivamente chiamato prima di quello.
Ciò significa anche che non puoi usare variabili all'interno della factory che sono definite al di fuori della factory.
Se hai bisogno di usare variabili all'interno della factory, prova vi.doMock
. Funziona allo stesso modo ma non viene sollevato. Attenzione che mocka solo le 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 mockando un modulo con un export di default, 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 che stai mockando, 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 mockando una dipendenza, Vitest cercherà di trovare una cartella __mocks__
nella root del progetto (il default è 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:
// increment.test.js
import { vi } from 'vitest';
// axios è un export di default 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
Attenzione che se non chiami vi.mock
, i moduli non vengono mockati automaticamente. 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 automockera 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 (hoisted) in cima al file, quindi puoi fare riferimento a variabili nello scope globale del file. La prossima importazione dinamica del modulo verrà mockata.
WARNING
Questo non mockera i moduli che sono stati importati prima che questo fosse chiamato. Non dimenticare che tutte le importazioni statiche in ESM sono sempre sollevate (hoisted), quindi mettere 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';
// ./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('importare il 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 siano mockati. Puoi passare { deep: true }
come secondo argomento per dire a TypeScript che l'intero oggetto è mockato, se lo è effettivamente.
// example.ts
export function add(x: number, y: number): number {
return x + y;
}
export function fetchSomething(): Promise<Response> {
return fetch('https://vitest.dev/');
}
// example.test.ts
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('mock del valore di ritorno 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 un modulo, bypassando tutti i controlli se deve essere mockato. Può essere utile se vuoi mockare un modulo parzialmente.
vi.mock('./example.js', async () => {
const axios = await vi.importActual('./example.js');
return { ...axios, 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 per importare restituiranno il modulo originale anche se era stato mockato prima. Questa chiamata viene sollevata (hoisted) in cima al file, quindi smockera solo i moduli che sono stati definiti nei setupFiles
, ad esempio.
vi.doUnmock
- Tipo:
(path: string | Promise<Module>) => void
Uguale a vi.unmock
, ma non viene sollevato (hoisted) in cima al file. La prossima importazione del modulo importerà il modulo originale invece del mock. Questo non smockera i moduli importati in precedenza.
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment è già mockato, perché vi.mock è sollevato (hoisted)
increment(1) === 100;
// questo è sollevato (hoisted), 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 (hoisted), quindi un'altra importazione restituirà il 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 moduli in cui lo stato locale entra 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 lo stato vecchio', 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. Utile, se hai una chiamata sincrona che avvia l'importazione di un modulo che altrimenti non puoi 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à fino a quando non saranno tutte 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.
Mock di Funzioni e Oggetti
Questa sezione descrive come lavorare con i mock di metodi e sostituire variabili ambientali e globali.
vi.fn
- Tipo:
(fn?: Function) => Mock
Crea uno spy su una funzione, anche se può essere avviato senza uno. Ogni volta che una funzione viene invocata, memorizza i suoi argomenti di chiamata, i ritorni e le istanze. Inoltre, puoi 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.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
Chiamerà .mockClear()
su tutti gli spy. Questo cancellerà la cronologia dei mock, ma non ripristinerà la sua implementazione a quella predefinita.
vi.resetAllMocks
Chiamerà .mockReset()
su tutti gli spy. Questo cancellerà la cronologia dei mock e ripristinerà la sua implementazione a una funzione vuota (restituirà undefined
).
vi.restoreAllMocks
Chiamerà .mockRestore()
su tutti gli spy. Questo cancellerà la cronologia dei mock e ripristinerà la sua implementazione a quella originale.
vi.spyOn
- Tipo:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Crea uno spy su un metodo o 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
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 sarai in grado di cambiare 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 metodi esportati in Modalità Browser. Invece, puoi spiare ogni metodo esportato chiamando vi.mock("./file-path.js", { spy: true })
. Questo mockera ogni export ma manterrà intatta la sua implementazione, 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 mentre è 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 di una variabile ambientale 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 altre 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à, fino a quando 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 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à, fino a quando 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 Falsi
Questa sezione descrive come lavorare con i timer falsi.
vi.advanceTimersByTime
- Tipo:
(ms: number) => Vitest
Questo metodo invocherà ogni timer avviato fino a quando non saranno trascorsi il numero specificato di millisecondi o la coda sarà vuota - qualunque cosa accada 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 fino a quando non saranno trascorsi il numero specificato di millisecondi o la coda sarà vuota - qualunque cosa accada 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 fare asserzioni tra ogni chiamata del timer. Puoi concatenare le chiamate per gestire i timer da solo.
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 fare 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 pianificati 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 che sono pianificati per l'esecuzione. Questi timer non verranno mai eseguiti in futuro.
vi.getMockedSystemTime
- Tipo:
() => Date | null
Restituisce la data corrente mockata che è stata impostata usando setSystemTime
. Se la data non è mockata, il metodo restituirà null
.
vi.getRealSystemTime
- Tipo:
() => number
Quando si usa vi.useFakeTimers
, le chiamate a Date.now
vengono mockate. Se hai bisogno di ottenere il tempo reale in millisecondi, puoi chiamare questa funzione.
vi.runAllTicks
- Tipo:
() => Vitest
Chiama ogni microtask che è stato messo in coda da process.nextTick
. Questo eseguirà anche tutti i microtask pianificati da soli.
vi.runAllTimers
- Tipo:
() => Vitest
Questo metodo invocherà ogni timer avviato fino a quando la coda dei timer non sarà vuota. Ciò significa che ogni timer chiamato durante runAllTimers
verrà attivato. Se hai un intervallo infinito, lancerà un errore dopo 10.000 tentativi (può essere configurato 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 fino a quando la coda dei timer non sarà vuota. Ciò significa che ogni timer chiamato durante runAllTimersAsync
verrà attivato, anche i timer asincroni. Se hai un intervallo infinito, lancerà un errore dopo 10.000 tentativi (può essere configurato 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
, anche 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 falsi sono abilitati, questo metodo simula un utente che cambia l'orologio di sistema (influenzando le API relative alla data come hrtime
, performance.now
o new Date()
) - tuttavia, non attiverà alcun timer. Se i timer falsi non sono abilitati, questo metodo mockera solo le chiamate a Date.*
.
Utile se hai bisogno di testare qualcosa che dipende dalla data corrente - ad esempio le chiamate Luxon all'interno del tuo 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, devi chiamare questo metodo. Avvolgerà tutte le chiamate future ai timer (come setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
e Date
) fino a quando 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 mocka automaticamente process.nextTick
. Ma puoi abilitarlo specificando l'opzione nell'argomento toFake
: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers
- Tipo:
() => boolean
Restituisce true
se i timer falsi sono abilitati.
vi.useRealTimers
- Tipo:
() => Vitest
Quando i timer sono esauriti, puoi chiamare questo metodo per ripristinare i timer mockati alle loro implementazioni originali. Tutti i timer che erano stati pianificati 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 fino a quando non avrà successo o scadrà il tempo.
Questo è molto utile quando devi attendere il completamento di un'azione asincrona, ad esempio, quando avvii un server e devi 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 default è 1000
interval: 20, // il default è 50
}
);
expect(server.isReady).toBe(true);
});
Funziona anche per callback asincroni
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('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 default è 1000
interval: 20, // il default è 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à fino a quando non verrà restituito un valore truthy. Questo è utile quando devi attendere che qualcosa esista prima di fare il passo successivo.
Guarda l'esempio qui sotto. 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 default è 1000
interval: 20, // il default è 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 (hoisted) 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 bypassare questa limitazione, puoi riscrivere le importazioni statiche in importazioni dinamiche in questo modo:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
Quando esegui vitest
, puoi farlo automaticamente usando il metodo vi.hoisted
.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
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);
Nota che questo metodo può anche essere chiamato in modo asincrono anche se il tuo ambiente non supporta il top-level await:
const promised = 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.