Vi
Vitest stellt Hilfsfunktionen über seine vi
-Funktion bereit. Du kannst global darauf zugreifen (wenn die Globals-Konfiguration aktiviert ist) oder sie direkt von vitest
importieren:
import { vi } from 'vitest';
Module mocken
Dieser Abschnitt beschreibt die API, die du verwenden kannst, wenn du ein Modul mocken möchtest. Beachte, dass Vitest das Mocken von Modulen, die mit require()
importiert wurden, nicht unterstützt.
vi.mock
- Typ:
(path: string, factory?: (importOriginal: () => unknown) => unknown) => void
Ersetzt alle Importe des Moduls unter dem angegebenen path
durch ein anderes Modul. Du kannst konfigurierte Vite-Aliase innerhalb eines Pfades verwenden. Der Aufruf von vi.mock
wird hochgezogen (hoisted), daher ist es unerheblich, wo du ihn aufrufst. Er wird immer vor allen Importen ausgeführt. Wenn du auf Variablen außerhalb seines Gültigkeitsbereichs verweisen musst, kannst du diese innerhalb von vi.hoisted
definieren und innerhalb von vi.mock
darauf verweisen.
WARNING
vi.mock
funktioniert nur für Module, die mit dem Schlüsselwort import
importiert wurden. Es funktioniert nicht mit require
.
Um vi.mock
zu hoisten, analysiert Vitest deine Dateien statisch. Dies bedeutet, dass vi
, das nicht direkt aus dem vitest
-Paket importiert wurde (z.B. aus einer Utility-Datei), nicht funktioniert. Verwende vi.mock
mit vi
, das von vitest
importiert wurde, oder aktiviere die Konfigurationsoption globals
.
Vitest wird keine Module mocken, die innerhalb einer Setup-Datei importiert wurden, da diese zwischengespeichert werden, wenn eine Testdatei ausgeführt wird. Du kannst vi.resetModules()
innerhalb von vi.hoisted
aufrufen, um alle Modul-Caches zu löschen, bevor eine Testdatei ausgeführt wird.
WARNING
Der Browser-Modus unterstützt derzeit keine Mock-Module. Du kannst diese Funktion im GitHub Issue verfolgen.
Wenn factory
definiert ist, geben alle Importe dessen Ergebnis zurück. Vitest ruft die Factory nur einmal auf und speichert die Ergebnisse für alle nachfolgenden Importe zwischen, bis vi.unmock
oder vi.doUnmock
aufgerufen wird.
Anders als in jest
kann die Factory asynchron sein. Du kannst vi.importActual
oder eine Hilfsfunktion mit der als erstes Argument übergebenen Factory verwenden und das ursprüngliche Modul darin erhalten.
import { vi } from 'vitest';
// ---cut---
// bei Verwendung von JavaScript
vi.mock('./path/to/module.js', async importOriginal => {
const mod = await importOriginal();
return {
...mod,
// einige Exporte ersetzen
namedExport: vi.fn(),
};
});
// bei Verwendung von TypeScript
vi.mock('./path/to/module.js', async importOriginal => {
const mod = await importOriginal<typeof import('./path/to/module.js')>();
return {
...mod,
// einige Exporte ersetzen
namedExport: vi.fn(),
};
});
WARNING
vi.mock
wird an den Anfang der Datei hochgezogen (anders ausgedrückt, verschoben). Das bedeutet, dass es, egal wo du es schreibst (sei es innerhalb von beforeEach
oder test
), tatsächlich vorher aufgerufen wird.
Dies bedeutet auch, dass innerhalb der Factory keine Variablen verwendet werden können, die außerhalb der Factory definiert sind.
Wenn du Variablen innerhalb der Factory verwenden musst, versuche vi.doMock
. Es funktioniert auf die gleiche Weise, wird aber nicht hochgezogen. Beachte, dass es nur nachfolgende Importe mockt.
Du kannst auch auf Variablen verweisen, die von der vi.hoisted
-Methode definiert wurden, wenn sie vor vi.mock
deklariert wurde:
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
Wenn du ein Modul mit einem Standardexport mocken möchtest, musst du einen default
-Schlüssel innerhalb des zurückgegebenen Factory-Funktionsobjekts angeben. Dies ist eine Besonderheit von ES-Modulen; die jest
-Dokumentation kann sich daher unterscheiden, da jest
CommonJS-Module verwendet. Zum Beispiel:
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// etc...
};
});
Wenn es einen __mocks__
-Ordner neben einer Datei gibt, die du mocken möchtest, und die Factory nicht bereitgestellt wird, versucht Vitest, eine Datei mit demselben Namen im __mocks__
-Unterordner zu finden und sie als tatsächliches Modul zu verwenden. Wenn du eine Abhängigkeit mocken möchtest, versucht Vitest, einen __mocks__
-Ordner im Root des Projekts zu finden (Standard ist process.cwd()
). Du kannst Vitest mitteilen, wo sich die Abhängigkeiten befinden, über die Konfigurationsoption deps.moduleDirectories.
Zum Beispiel hast du diese Dateistruktur:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Wenn du vi.mock
in einer Testdatei aufrufst, ohne eine Factory bereitzustellen, wird eine Datei im __mocks__
-Ordner gesucht, die als Modul verwendet werden soll:
// increment.test.js
import { vi } from 'vitest';
// axios ist ein Standardexport von `__mocks__/axios.js`
import axios from 'axios';
// increment ist ein benannter Export von `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Beachte, dass Module nicht automatisch gemockt werden, wenn du vi.mock
nicht aufrufst. Um das Automocking-Verhalten von Jest zu replizieren, kannst du vi.mock
für jedes erforderliche Modul innerhalb von setupFiles
aufrufen.
Wenn kein __mocks__
-Ordner vorhanden ist oder keine Factory bereitgestellt wird, importiert Vitest das ursprüngliche Modul und mockt automatisch alle seine Exporte. Für die angewendeten Regeln siehe Verfahren.
vi.doMock
- Typ:
(path: string, factory?: (importOriginal: () => unknown) => unknown) => void
Das gleiche wie vi.mock
, aber es wird nicht an den Anfang der Datei hochgezogen, sodass du auf Variablen im globalen Dateibereich verweisen kannst. Der nächste dynamische Import des Moduls wird gemockt.
WARNING
Dies mockt keine Module, die importiert wurden, bevor dies aufgerufen wurde. Vergiss nicht, dass alle statischen Importe in ESM immer hochgezogen werden, sodass das Platzieren vor einem statischen Import nicht erzwingt, dass er vor dem Import aufgerufen wird:
vi.doMock('./increment.js'); // dies wird _erst nach_ der Importanweisung aufgerufen
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// das Modul wird nicht gemockt, da vi.doMock noch nicht aufgerufen wurde
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// Sie können auf Variablen innerhalb einer Factory zugreifen
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('importing the next module imports mocked one', async () => {
// der ursprüngliche Import wurde NICHT gemockt, da `vi.doMock` erst NACH den Importen ausgewertet wird
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// neuer dynamischer Import gibt gemocktes Modul zurück
expect(mockedIncrement(1)).toBe(101);
expect(mockedIncrement(1)).toBe(102);
expect(mockedIncrement(1)).toBe(103);
});
vi.mocked
- Typ:
<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
- Typ:
<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
TypeScript-Hilfe für Typen. Gibt nur das übergebene Objekt zurück.
Wenn partial
true
ist, wird ein Partial<T>
als Rückgabewert erwartet. Standardmäßig lässt dies TypeScript nur glauben, dass die Werte der ersten Ebene gemockt sind. Du kannst { deep: true }
als zweites Argument übergeben, um TypeScript mitzuteilen, dass das gesamte Objekt gemockt ist, wenn dies tatsächlich der Fall ist.
import example from './example.js';
vi.mock('./example.js');
test('1 + 1 equals 10', async () => {
vi.mocked(example.calc).mockReturnValue(10);
expect(example.calc(1, '+', 1)).toBe(10);
});
vi.importActual
- Typ:
<T>(path: string) => Promise<T>
Importiert ein Modul und umgeht alle Prüfungen, ob es gemockt werden soll. Kann nützlich sein, wenn du ein Modul teilweise mocken möchtest.
vi.mock('./example.js', async () => {
const axios = await vi.importActual('./example.js');
return { ...axios, get: vi.fn() };
});
vi.importMock
- Typ:
<T>(path: string) => Promise<MaybeMockedDeep<T>>
Importiert ein Modul, wobei alle seine Eigenschaften (einschließlich verschachtelter Eigenschaften) gemockt werden. Befolgt die gleichen Regeln wie vi.mock
. Für die angewendeten Regeln siehe Verfahren.
vi.unmock
- Typ:
(path: string) => void
Entfernt ein Modul aus der Mock-Registry. Alle Aufrufe von import geben das ursprüngliche Modul zurück, auch wenn es zuvor gemockt wurde. Dieser Aufruf wird an den Anfang der Datei gehoistet, sodass nur Module unmocked werden, die beispielsweise in setupFiles
definiert wurden.
vi.doUnmock
- Typ:
(path: string) => void
Das gleiche wie vi.unmock
, wird aber nicht an den Anfang der Datei hochgezogen. Der nächste Import des Moduls importiert das ursprüngliche Modul anstelle des Mocks. Dies unmocked keine zuvor importierten Module.
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment ist bereits gemockt, da vi.mock gehoistet wird
increment(1) === 100;
// dies wird gehoistet, und die Factory wird vor dem Import in Zeile 1 aufgerufen
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// alle Aufrufe werden gemockt, und `increment` gibt immer 100 zurück
increment(1) === 100;
increment(30) === 100;
// dies wird nicht gehoistet, sodass ein anderer Import ein "unmocked" Modul zurückgibt
vi.doUnmock('./increment.js');
// dies gibt IMMER NOCH 100 zurück, weil `vi.doUnmock` ein Modul nicht neu bewertet
increment(1) === 100;
increment(30) === 100;
// der nächste Import ist unmocked, jetzt ist `increment` die ursprüngliche Funktion, die count + 1 zurückgibt
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- Typ:
() => Vitest
Setzt die Modul-Registry zurück, indem der Cache aller Module geleert wird. Dies ermöglicht es, Module neu zu bewerten, wenn sie erneut importiert werden. Top-Level-Importe können nicht neu bewertet werden. Kann nützlich sein, um Module zu isolieren, bei denen lokale Zustände zwischen Tests in Konflikt stehen.
import { vi } from 'vitest';
import { data } from './data.js'; // Wird nicht vor jedem Test erneut ausgewertet
beforeEach(() => {
vi.resetModules();
});
test('change state', async () => {
const mod = await import('./some/path.js'); // Wird neu bewertet
mod.changeLocalState('new value');
expect(mod.getLocalState()).toBe('new value');
});
test('module has old state', async () => {
const mod = await import('./some/path.js'); // Wird neu bewertet
expect(mod.getLocalState()).toBe('old value');
});
WARNING
Setzt die Mock-Registry nicht zurück. Um die Mock-Registry zu leeren, verwende vi.unmock
oder vi.doUnmock
.
vi.dynamicImportSettled
Wartet, bis alle dynamischen Importe abgeschlossen sind. Nützlich, wenn du einen synchronen Aufruf hast, der einen dynamischen Import startet, auf den du sonst nicht warten kannst.
import { expect, test } from 'vitest';
// kann Import nicht verfolgen, da Promise nicht zurückgegeben wird
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('operations are resolved', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
Wenn während eines dynamischen Imports ein weiterer dynamischer Import initiiert wird, wartet diese Methode, bis alle aufgelöst sind.
Diese Methode wartet auch auf den nächsten setTimeout
-Tick, nachdem der Import aufgelöst wurde, sodass alle synchronen Operationen abgeschlossen sein sollten, wenn er aufgelöst wird.
Mocken von Funktionen und Objekten
Dieser Abschnitt beschreibt, wie man mit Methoden-Mocks arbeitet und Umgebungs- und globale Variablen ersetzt.
vi.fn
- Typ:
(fn?: Function) => Mock
Erstellt einen Spion für eine Funktion, kann aber auch ohne eine solche initialisiert werden. Jedes Mal, wenn eine Funktion aufgerufen wird, speichert sie ihre Aufrufargumente, Rückgabewerte und Instanzen. Außerdem kannst du ihr Verhalten mit Methoden manipulieren. Wenn keine Funktion angegeben wird, gibt Mock undefined
zurück, wenn er aufgerufen wird.
import { expect, vi } from 'vitest';
// ---cut---
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
- Typ:
(fn: Function) => boolean
Prüft, ob ein gegebener Parameter eine Mock-Funktion ist. Wenn du TypeScript verwendest, wird auch sein Typ eingeschränkt.
vi.clearAllMocks
Ruft .mockClear()
auf allen Spionen auf. Dadurch wird der Mock-Verlauf geleert, aber die Implementierung nicht auf die Standardeinstellung zurückgesetzt.
vi.resetAllMocks
Ruft .mockReset()
auf allen Spionen auf. Dadurch wird der Mock-Verlauf geleert und die Implementierung auf eine leere Funktion zurückgesetzt (gibt undefined
zurück).
vi.restoreAllMocks
Ruft .mockRestore()
auf allen Spionen auf. Dadurch wird der Mock-Verlauf geleert und die Implementierung auf die ursprüngliche zurückgesetzt.
vi.spyOn
- Typ:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Erstellt einen Spion für eine Methode oder einen Getter/Setter eines Objekts, ähnlich wie vi.fn()
. Es gibt eine Mock-Funktion zurück.
import { expect, vi } from 'vitest';
// ---cut---
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
Du kannst vi.restoreAllMocks
innerhalb von afterEach
aufrufen (oder test.restoreMocks
aktivieren), um alle Methoden auf ihre ursprünglichen Implementierungen zurückzusetzen. Dadurch wird die ursprüngliche Objektbeschreibung wiederhergestellt, sodass du die Implementierung der Methode nicht ändern kannst:
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()); // still 42!
vi.stubEnv 0.26.0+
- Typ:
(name: string, value: string) => Vitest
Ändert den Wert der Umgebungsvariable in process.env
und import.meta.env
. Du kannst ihren Wert wiederherstellen, indem du vi.unstubAllEnvs
aufrufst.
import { vi } from 'vitest';
// `process.env.NODE_ENV` und `import.meta.env.NODE_ENV`
// `process.env.NODE_ENV` und `import.meta.env.NODE_ENV` haben den Wert "development", bevor `vi.stubEnv` aufgerufen wird
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
// ändert keine anderen Umgebungsvariable
import.meta.env.MODE === 'development';
TIP
Du kannst den Wert auch ändern, indem du ihn einfach zuweist, aber du kannst vi.unstubAllEnvs
nicht verwenden, um den vorherigen Wert wiederherzustellen:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs 0.26.0+
- Typ:
() => Vitest
Stellt alle import.meta.env
- und process.env
-Werte wieder her, die mit vi.stubEnv
geändert wurden. Wenn es zum ersten Mal aufgerufen wird, merkt sich Vitest den ursprünglichen Wert und speichert ihn, bis unstubAllEnvs
erneut aufgerufen wird.
import { vi } from 'vitest';
// `process.env.NODE_ENV` und `import.meta.env.NODE_ENV`
// `process.env.NODE_ENV` und `import.meta.env.NODE_ENV` haben den Wert "development", bevor `stubEnv` aufgerufen wird
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();
// stellt den Wert wieder her, der vor dem ersten "stubEnv"-Aufruf gespeichert wurde
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Typ:
(name: string | number | symbol, value: unknown) => Vitest
Ändert den Wert der globalen Variablen. Du kannst ihren ursprünglichen Wert wiederherstellen, indem du vi.unstubAllGlobals
aufrufst.
import { vi } from 'vitest';
// `innerWidth` hat den Wert "0", bevor `stubGlobal` aufgerufen wird
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// wenn Sie jsdom oder happy-dom verwenden
window.innerWidth === 100;
TIP
Du kannst den Wert auch ändern, indem du ihn einfach globalThis
oder window
zuweist (wenn du die jsdom
- oder happy-dom
-Umgebung verwendest), aber du kannst vi.unstubAllGlobals
nicht verwenden, um den ursprünglichen Wert wiederherzustellen:
globalThis.innerWidth = 100;
// wenn Sie jsdom oder happy-dom verwenden
window.innerWidth = 100;
vi.unstubAllGlobals 0.26.0+
- Typ:
() => Vitest
Stellt alle globalen Werte in globalThis
/global
(und window
/top
/self
/parent
, wenn du die jsdom
- oder happy-dom
-Umgebung verwendest) wieder her, die mit vi.stubGlobal
geändert wurden. Wenn es zum ersten Mal aufgerufen wird, merkt sich Vitest den ursprünglichen Wert und speichert ihn, bis unstubAllGlobals
erneut aufgerufen wird.
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver hat den Wert "undefined", bevor `stubGlobal` aufgerufen wird
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// wenn Sie jsdom oder happy-dom verwenden
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// wirft einen ReferenceError, da es nicht definiert ist
IntersectionObserver === undefined;
Fake-Timer
Dieser Abschnitt beschreibt die Arbeit mit Fake-Timern.
vi.advanceTimersByTime
- Typ:
(ms: number) => Vitest
Diese Methode führt alle initialisierten Timer aus, bis die angegebene Anzahl von Millisekunden vergangen ist oder die Timer-Warteschlange leer ist, je nachdem, was zuerst eintritt.
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- Typ:
(ms: number) => Promise<Vitest>
Diese Methode führt alle initialisierten Timer aus, bis die angegebene Anzahl von Millisekunden vergangen ist oder die Timer-Warteschlange leer ist, je nachdem, was zuerst eintritt. Asynchron gesetzte Timer werden ebenfalls berücksichtigt.
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersByTimeAsync(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- Typ:
() => Vitest
Führt den nächsten verfügbaren Timer aus. Nützlich, um zwischen den einzelnen Timer-Ausführungen Überprüfungen durchzuführen. Sie können die Aufrufe verketten, um die Timer-Ausführung präzise zu steuern.
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Typ:
() => Promise<Vitest>
Führt den nächsten verfügbaren Timer aus und wartet, bis er abgeschlossen ist, falls er asynchron gesetzt wurde. Nützlich, um zwischen den einzelnen Timer-Ausführungen Überprüfungen durchzuführen.
import { expect, vi } from 'vitest';
// ---cut---
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.getTimerCount
- Typ:
() => number
Gibt die Anzahl der ausstehenden Timer zurück.
vi.clearAllTimers
Entfernt alle Timer, die zur Ausführung geplant sind. Diese Timer werden in Zukunft nicht mehr ausgeführt.
vi.getMockedSystemTime
- Typ:
() => Date | null
Gibt das gemockte aktuelle Datum zurück, das mit setSystemTime
gesetzt wurde. Wenn das Datum nicht gemockt ist, gibt die Methode null
zurück.
vi.getRealSystemTime
- Typ:
() => number
Wenn vi.useFakeTimers
verwendet wird, werden Date.now
-Aufrufe gemockt. Wenn Sie die tatsächliche Systemzeit in Millisekunden benötigen, können Sie diese Funktion aufrufen.
vi.runAllTicks
- Typ:
() => Vitest
Führt alle Mikrotasks aus, die durch process.nextTick
in die Warteschlange gestellt wurden. Dadurch werden auch alle Mikrotasks ausgeführt, die von diesen selbst geplant wurden.
vi.runAllTimers
- Typ:
() => Vitest
Diese Methode führt alle initialisierten Timer aus, bis die Timer-Warteschlange leer ist. Das bedeutet, dass jeder Timer, der während der Ausführung von runAllTimers
ausgelöst wird, ebenfalls ausgeführt wird. Wenn ein Intervall eine Endlosschleife verursacht, wird nach 10.000 Versuchen ein Fehler ausgelöst (dieser Grenzwert kann mit fakeTimers.loopLimit
konfiguriert werden).
import { vi } from 'vitest';
// ---cut---
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
- Typ:
() => Promise<Vitest>
Diese Methode führt alle initialisierten Timer asynchron aus, bis die Timer-Warteschlange leer ist. Das bedeutet, dass jeder Timer, der während der Ausführung von runAllTimersAsync
ausgelöst wird, ebenfalls ausgeführt wird, auch asynchrone Timer. Wenn ein Intervall eine Endlosschleife verursacht, wird nach 10.000 Versuchen ein Fehler ausgelöst (dieser Grenzwert kann mit fakeTimers.loopLimit
konfiguriert werden).
import { vi } from 'vitest';
// ---cut---
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- Typ:
() => Vitest
Diese Methode führt alle Timer aus, die nach dem Aufruf von vi.useFakeTimers
initialisiert wurden. Timer, die während der Ausführung initialisiert werden, werden nicht ausgeführt.
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Typ:
() => Promise<Vitest>
Diese Methode führt alle Timer asynchron aus, die nach dem Aufruf von vi.useFakeTimers
initialisiert wurden, einschließlich asynchroner Timer. Timer, die während der Ausführung initialisiert werden, werden nicht ausgeführt.
import { vi } from 'vitest';
// ---cut---
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
- Typ:
(date: string | number | Date) => void
Wenn Fake-Timer aktiviert sind, simuliert diese Methode eine Änderung der Systemuhr durch den Benutzer (was sich auf datumsbezogene APIs wie hrtime
, performance.now
oder new Date()
auswirkt). Timer werden jedoch nicht ausgelöst. Wenn Fake-Timer nicht aktiviert sind, mockt diese Methode nur Date.*
-Aufrufe.
Nützlich, wenn Sie etwas testen möchten, das vom aktuellen Datum abhängt, zum Beispiel Luxon-Aufrufe in Ihrem Code.
import { expect, vi } from 'vitest';
// ---cut---
const date = new Date(1998, 11, 19);
vi.useFakeTimers();
vi.setSystemTime(date);
expect(Date.now()).toBe(date.valueOf());
vi.useRealTimers();
vi.useFakeTimers
- Typ:
(config?: FakeTimerInstallOpts) => Vitest
Um das Mocken von Timern zu aktivieren, müssen Sie diese Methode aufrufen. Diese Methode fängt alle nachfolgenden Aufrufe von Timern ab (wie setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
und Date
), bis vi.useRealTimers()
aufgerufen wird.
Das Mocken von nextTick
wird nicht unterstützt, wenn Vitest mit --pool=forks
innerhalb von node:child_process
ausgeführt wird. NodeJS verwendet process.nextTick
intern in node:child_process
und bleibt hängen, wenn es gemockt wird. Das Mocken von nextTick
wird unterstützt, wenn Vitest mit --pool=threads
ausgeführt wird.
Die interne Implementierung basiert auf @sinonjs/fake-timers
.
TIP
Seit Version 0.35.0
wird process.nextTick
nicht mehr automatisch von vi.useFakeTimers()
gemockt. Es kann weiterhin gemockt werden, indem die Option im toFake
-Argument angegeben wird: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers 0.34.5+
- Typ:
() => boolean
Gibt true
zurück, wenn Fake-Timer aktiviert sind.
vi.useRealTimers
- Typ:
() => Vitest
Nachdem die Timer verwendet wurden, kann diese Methode aufgerufen werden, um die simulierten Timer auf ihre ursprünglichen Implementierungen zurückzusetzen. Alle zuvor geplanten Timer werden verworfen.
Sonstiges
Eine Sammlung nützlicher Hilfsfunktionen, die Vitest bereitstellt.
vi.waitFor 0.34.5+
- Typ:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Wartet, bis der Callback erfolgreich ausgeführt wurde. Wenn der Callback einen Fehler auslöst oder ein Promise ablehnt, wird so lange gewartet, bis er erfolgreich ist oder ein Timeout eintritt.
Dies ist sehr nützlich, wenn Sie warten müssen, bis eine asynchrone Aktion abgeschlossen ist, z. B. wenn Sie einen Server starten und warten müssen, bis er bereit ist.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Server started successfully', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) throw new Error('Server not started');
console.log('Server started');
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
);
expect(server.isReady).toBe(true);
});
Es funktioniert auch für asynchrone Callbacks
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('Element exists in a DOM', async () => {
// start populating DOM
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// try to get the element until it exists
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Wenn vi.useFakeTimers
verwendet wird, ruft vi.waitFor
automatisch vi.advanceTimersByTime(interval)
in jedem Check-Callback auf.
vi.waitUntil 0.34.5+
- Typ:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Dies ähnelt vi.waitFor
, aber wenn der Callback einen Fehler auslöst, wird die Ausführung sofort unterbrochen und eine Fehlermeldung ausgegeben. Wenn der Callback einen Falsy-Wert zurückgibt, wird die nächste Prüfung so lange wiederholt, bis ein Truthy-Wert zurückgegeben wird. Dies ist nützlich, wenn Sie warten müssen, bis etwas vorhanden ist, bevor Sie den nächsten Schritt ausführen.
Betrachten Sie das folgende Beispiel. Wir können vi.waitUntil
verwenden, um zu warten, bis das Element auf der Seite angezeigt wird, und dann können wir etwas mit dem Element tun.
import { expect, test, vi } from 'vitest';
test('Element render correctly', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // default is 1000
interval: 20, // default is 50
});
// do something with the element
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted 0.31.0+
- Typ:
<T>(factory: () => T) => T
In ES-Modulen werden alle statischen import
-Anweisungen an den Anfang der Datei verschoben (hoisted), sodass Code, der vor den Imports definiert ist, erst nach der Auswertung der Imports ausgeführt wird.
Es kann jedoch nützlich sein, einige Seiteneffekte, wie das Mocken von Daten, aufzurufen, bevor ein Modul importiert wird.
Um diese Einschränkung zu umgehen, können Sie statische Imports in dynamische umschreiben, wie folgt:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
Wenn Sie vitest
ausführen, können Sie dies automatisch tun, indem Sie die Methode vi.hoisted
verwenden.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
Diese Methode gibt den Wert zurück, der von der Factory zurückgegeben wurde. Sie können diesen Wert in Ihren vi.mock
-Factories verwenden, wenn Sie einfachen Zugriff auf lokal definierte Variablen benötigen:
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);
Diese Methode kann auch asynchron aufgerufen werden, selbst wenn Ihre Umgebung kein Top-Level-Await unterstützt:
const promised = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- Typ:
RuntimeConfig
Diese Methode aktualisiert die Konfiguration für die aktuelle Testdatei. Diese Methode unterstützt nur Konfigurationsoptionen, die sich auf die aktuelle Testdatei auswirken:
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// supports the whole object
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// supports only "sequence.hooks"
},
});
vi.resetConfig
- Typ:
RuntimeConfig
Wenn vi.setConfig
zuvor aufgerufen wurde, wird die Konfiguration auf den Ausgangszustand zurückgesetzt.