Vi
Vitest stellt Hilfsfunktionen über den vi
-Helfer bereit. Sie können global darauf zugreifen (sofern die globale Konfiguration aktiviert ist) oder ihn direkt von vitest
importieren:
import { vi } from 'vitest';
Module mocken
Dieser Abschnitt beschreibt die API zum Mocken von Modulen. Beachten Sie, dass Vitest das Mocken von Modulen, die mit require()
importiert wurden, nicht unterstützt.
vi.mock
- Typ:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Typ:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Ersetzt alle Importe, die vom angegebenen path
stammen, durch ein anderes Modul. Sie können konfigurierte Vite-Aliase innerhalb eines Pfades verwenden. Der Aufruf von vi.mock
wird hochgezogen (hoisted), d.h. er wird immer vor allen Importen ausgeführt, unabhängig davon, wo Sie ihn aufrufen. Wenn Sie auf Variablen zugreifen müssen, die außerhalb seines Geltungsbereichs definiert sind, können Sie diese innerhalb von vi.hoisted
definieren und innerhalb von vi.mock
darauf verweisen.
WARNING
vi.mock
funktioniert nur für Module, die mit dem import
-Schlüsselwort importiert wurden. Es funktioniert nicht mit require
.
Um vi.mock
hochzuziehen, analysiert Vitest Ihre Dateien statisch. Dies bedeutet, dass ein vi
-Objekt, das nicht direkt aus dem vitest
-Paket importiert wurde (z. B. aus einer Hilfsdatei), nicht verwendet werden kann. Verwenden Sie vi.mock
mit vi
, das von vitest
importiert wurde, oder aktivieren Sie die Konfigurationsoption globals
.
Vitest mockt keine Module, die in einer Setup-Datei importiert wurden, da sie zum Zeitpunkt der Ausführung einer Testdatei bereits zwischengespeichert sind. Sie können vi.resetModules()
innerhalb von vi.hoisted
aufrufen, um alle Modul-Caches vor der Ausführung einer Testdatei zu leeren.
Wenn die factory
-Funktion definiert ist, geben alle Importe ihr Ergebnis zurück. Vitest ruft die Factory nur einmal auf und speichert die Ergebnisse für alle nachfolgenden Importe im Cache, bis vi.unmock
oder vi.doUnmock
aufgerufen wird.
Im Gegensatz zu Jest kann die Factory asynchron sein. Sie können vi.importActual
oder einen Helfer verwenden, indem Sie die Factory als erstes Argument übergeben, um das ursprüngliche Modul darin zu erhalten.
Sie können auch ein Objekt mit einer spy
-Eigenschaft anstelle einer Factory-Funktion bereitstellen. Wenn spy
true
ist, wird Vitest das Modul wie gewohnt automocken, dabei aber die Implementierung der Exporte nicht überschreiben. Dies ist nützlich, um zu überprüfen, ob die exportierte Methode korrekt von einer anderen Methode aufgerufen wurde.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// ruft die ursprüngliche Implementierung auf,
// ermöglicht aber später die Überprüfung des Verhaltens
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Vitest unterstützt auch ein Promise eines Moduls anstelle eines Strings in den Methoden vi.mock
und vi.doMock
für eine bessere IDE-Unterstützung. Wenn die Datei verschoben wird, wird der Pfad aktualisiert, wobei importOriginal
den Typ automatisch erbt. Die Verwendung dieser Signatur erzwingt auch, dass der Rückgabetyp der Factory mit dem ursprünglichen Modul kompatibel ist (wobei Exporte optional bleiben).
// @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(); // Typ wird abgeleitet
// ^?
return {
...mod,
// einige Exporte ersetzen
total: vi.fn(),
};
});
Intern arbeitet Vitest immer noch mit einem String und nicht mit einem Modulobjekt.
Wenn Sie jedoch TypeScript mit in tsconfig.json
konfigurierten Pfad-Aliasen verwenden, kann der Compiler die Importtypen nicht korrekt auflösen. Damit dies funktioniert, stellen Sie sicher, dass alle aliased Imports durch ihre entsprechenden relativen Pfade ersetzt werden. Z.B. verwenden Sie import('./path/to/module.js')
anstelle von import('@/module')
.
WARNING
vi.mock
wird an den Anfang der Datei hochgezogen (mit anderen Worten, verschoben). Das bedeutet, dass es immer dann aufgerufen wird, wenn Sie es schreiben (sei es innerhalb von beforeEach
oder test
), tatsächlich vorher aufgerufen wird.
Das bedeutet auch, dass Sie innerhalb der Factory keine Variablen verwenden können, die außerhalb der Factory definiert sind.
Wenn Sie Variablen in der Factory verwenden müssen, versuchen Sie vi.doMock
. Es funktioniert auf die gleiche Weise, wird aber nicht hochgezogen. Beachten Sie, dass es nur nachfolgende Importe mockt.
Sie können 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 Sie ein Modul mit einem Standardexport mocken möchten, müssen Sie einen default
-Schlüssel innerhalb des zurückgegebenen Factory-Funktionsobjekts bereitstellen. Dies ist eine ES-Modul-spezifische Besonderheit; daher kann die Jest-Dokumentation abweichen, da Jest CommonJS-Module verwendet. Zum Beispiel:
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// etc...
};
});
Wenn sich ein __mocks__
-Ordner neben einer Datei befindet, die Sie mocken, und keine Factory bereitgestellt wird, versucht Vitest, eine Datei mit demselben Namen im Unterordner __mocks__
zu finden und diese als tatsächliches Modul zu verwenden. Wenn Sie eine Abhängigkeit mocken, versucht Vitest, einen __mocks__
-Ordner im Stammverzeichnis des Projekts zu finden (Standard ist process.cwd()
). Über die Konfigurationsoption deps.moduleDirectories
können Sie Vitest mitteilen, wo sich die Abhängigkeiten befinden.
Sie haben zum Beispiel diese Dateistruktur:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Wenn Sie vi.mock
in einer Testdatei ohne Factory oder Optionen aufrufen, findet es eine Datei im __mocks__
-Ordner, die als Modul verwendet werden soll:
import { vi } from 'vitest';
// axios ist ein Standardexport aus `__mocks__/axios.js`
import axios from 'axios';
// increment ist ein benannter Export aus `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Beachten Sie, dass Module nicht automatisch gemockt werden, wenn vi.mock
nicht aufgerufen wird. Um das Automocking-Verhalten von Jest nachzubilden, können Sie vi.mock
für jedes erforderliche Modul innerhalb von setupFiles
aufrufen.
Wenn weder ein __mocks__
-Ordner noch eine Factory bereitgestellt wird, importiert Vitest das ursprüngliche Modul und automockt alle seine Exporte. Die angewendeten Regeln finden Sie unter Algorithmus.
vi.doMock
- Typ:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Typ:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Dasselbe wie vi.mock
, aber es wird nicht an den Anfang der Datei hochgezogen, sodass Sie auf Variablen im globalen Dateibereich verweisen können. Das Modul wird beim nächsten dynamischen Import gemockt.
WARNING
Dies mockt keine Module, die vor diesem Aufruf importiert wurden. Vergessen Sie nicht, dass alle statischen Importe in ESM immer hochgezogen werden, sodass das Platzieren dieses vor einem statischen Import nicht erzwingt, dass es vor dem Import aufgerufen wird:
vi.doMock('./increment.js'); // dieser Aufruf wird _nach_ der Importanweisung aufgerufen
import { increment } from './increment.js';
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// das Modul ist 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('Importieren des nächsten Moduls importiert das gemockte', async () => {
// der ursprüngliche Import WURDE NICHT GEMOCKT, da vi.doMock NACH den Importen ausgewertet wird
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// der neue dynamische 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>
Typ-Helfer für TypeScript. Gibt einfach das übergebene Objekt zurück.
Wenn partial
true
ist, wird ein Partial<T>
als Rückgabewert erwartet. Standardmäßig geht TypeScript nur davon aus, dass die Werte der ersten Ebene gemockt sind. Sie können { deep: true }
als zweites Argument übergeben, um TypeScript mitzuteilen, dass das gesamte Objekt gemockt ist, sofern dies zutrifft.
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 ist gleich 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('Mock-Rückgabewert mit nur teilweise korrekter Typisierung', 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 }) // dies ist ein Typfehler
});
vi.importActual
- Typ:
<T>(path: string) => Promise<T>
Importiert ein Modul und umgeht dabei alle Prüfungen, ob es gemockt werden muss. Dies kann nützlich sein, wenn Sie ein Modul teilweise mocken möchten.
vi.mock('./example.js', async () => {
const originalModule = await vi.importActual('./example.js');
return { ...originalModule, get: vi.fn() };
});
vi.importMock
- Typ:
<T>(path: string) => Promise<MaybeMockedDeep<T>>
Importiert ein Modul, bei dem alle seine Eigenschaften (einschließlich verschachtelter Eigenschaften) gemockt sind. Folgt denselben Regeln wie vi.mock
. Die angewendeten Regeln finden Sie unter Algorithmus.
vi.unmock
- Typ:
(path: string | Promise<Module>) => void
Entfernt das Modul aus dem Mock-Register. Alle Aufrufe zum Importieren geben das ursprüngliche Modul zurück, auch wenn es zuvor gemockt wurde. Dieser Aufruf wird an den Anfang der Datei hochgezogen, sodass dadurch nur Module entmockt werden, die beispielsweise in setupFiles
definiert wurden.
vi.doUnmock
- Typ:
(path: string | Promise<Module>) => void
Dasselbe wie vi.unmock
, wird aber nicht an den Anfang der Datei hochgezogen. Beim nächsten Import des Moduls wird das ursprüngliche Modul anstelle des Mocks importiert. Diese Methode wird zuvor importierte Module nicht entmocken.
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment ist bereits gemockt, da vi.mock hochgezogen wurde
increment(1) === 100;
// dieser vi.mock-Aufruf wird hochgezogen, und die Factory wird vor dem Import in Zeile 1 aufgerufen
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// alle Aufrufe der Funktion sind gemockt, und `increment` gibt immer 100 zurück
increment(1) === 100;
increment(30) === 100;
// dies wird nicht hochgezogen, daher gibt ein anderer Import ein ungemocktes Modul zurück
vi.doUnmock('./increment.js');
// der Aufruf von increment gibt IMMER NOCH 100 zurück, da vi.doUnmock ein Modul nicht neu auswertet
increment(1) === 100;
increment(30) === 100;
// durch den nächsten Import ist das Modul ungemockt, 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 das Modulregister zurück, indem der Cache aller Module geleert wird. Dadurch können Module bei erneutem Import neu bewertet werden. Importe auf oberster Ebene können nicht neu bewertet werden. Dies kann nützlich sein, um Module zu isolieren, bei denen lokale Zustände zwischen Tests kollidieren.
import { vi } from 'vitest';
import { data } from './data.js'; // Wird nicht vor jedem Test neu bewertet
beforeEach(() => {
vi.resetModules();
});
test('Zustand ändern', async () => {
const mod = await import('./some/path.js'); // Wird erneut ausgewertet
mod.changeLocalState('neuer Wert');
expect(mod.getLocalState()).toBe('neuer Wert');
});
test('Modul hat alten Zustand', async () => {
const mod = await import('./some/path.js'); // Wird erneut ausgewertet
expect(mod.getLocalState()).toBe('alter Wert');
});
WARNING
Setzt das Mock-Register nicht zurück. Um das Mock-Register zu leeren, verwenden Sie vi.unmock
oder vi.doUnmock
.
vi.dynamicImportSettled
Wartet, bis alle dynamischen Importe geladen sind. Nützlich, wenn Sie einen synchronen Aufruf haben, der den Import eines Moduls startet, auf das Sie sonst nicht warten können.
import { expect, test } from 'vitest';
// Import kann nicht verfolgt werden, da kein Promise zurückgegeben wird
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('Operationen sind aufgelöst', 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 Importe 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, sobald der Import aufgelöst ist.
Funktionen und Objekte mocken
Dieser Abschnitt beschreibt, wie man mit Methoden-Mocks arbeitet und Umgebungs- und globale Variablen ersetzt.
vi.fn
- Typ:
(fn?: Function) => Mock
Erstellt einen Spy auf 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 können Sie das Verhalten des Spys mit Methoden manipulieren. Wenn keine Funktion angegeben wird, gibt der Mock undefined
zurück, sobald er aufgerufen wird.
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+
- Typ:
<T>(value: T) => MaybeMockedDeep<T>
Mockt Eigenschaften und Methoden eines gegebenen Objekts rekursiv auf die gleiche Weise, wie vi.mock()
Modulexporte mockt. Details finden Sie unter 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
- Typ:
(fn: Function) => boolean
Prüft, ob der übergebene Parameter eine Mock-Funktion ist. Wenn Sie TypeScript verwenden, wird dadurch auch der Typ eingegrenzt.
vi.clearAllMocks
Ruft .mockClear()
für alle Spies auf. Diese Methode löscht den Mock-Verlauf, ohne die Mock-Implementierungen zu beeinflussen.
vi.resetAllMocks
Ruft .mockReset()
für alle Spies auf. Dies löscht den Mock-Verlauf und setzt die Implementierung jedes Mocks auf ihre ursprüngliche zurück.
vi.restoreAllMocks
Ruft .mockRestore()
für alle Spies auf. Diese Methode löscht den Mock-Verlauf, stellt alle ursprünglichen Mock-Implementierungen wieder her und stellt die ursprünglichen Deskriptoren der gespionten Objekte wieder her.
vi.spyOn
- Typ:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Erstellt einen Spy auf eine Methode oder einen Getter/Setter eines Objekts, ähnlich wie vi.fn()
. Diese Methode gibt eine Mock-Funktion zurück.
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
In Umgebungen, die Explizites Ressourcenmanagement unterstützen, können Sie using
anstelle von const
verwenden, um mockRestore
automatisch für jede gemockte Funktion aufzurufen, wenn der enthaltende Block verlassen wird. Dies erweist sich als besonders nützlich für gespionte Methoden:
it('ruft console.log auf', () => {
using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
debug('Nachricht')
expect(spy).toHaveBeenCalled()
})
// console.log wird hier wiederhergestellt
TIP
Sie können vi.restoreAllMocks
innerhalb von afterEach
aufrufen oder test.restoreMocks
aktivieren, um alle Methoden auf ihre ursprünglichen Implementierungen zurückzusetzen. Dadurch wird der ursprüngliche Objekt-Deskriptor wiederhergestellt, sodass Sie die Implementierung der Methode nicht ändern können:
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()); // immer noch 42!
TIP
Es ist nicht möglich, exportierte Methoden im Browser-Modus auszuspionieren. Stattdessen können Sie jede exportierte Methode ausspionieren, indem Sie vi.mock("./file-path.js", { spy: true })
aufrufen. Dieser Aufruf mockt jeden Export, behält aber seine Implementierung intakt, sodass Sie überprüfen können, ob die Methode korrekt aufgerufen wurde.
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);
Und obwohl es möglich ist, Exporte in jsdom
oder anderen Node.js-Umgebungen auszuspionieren, könnte sich dies in Zukunft ändern.
vi.stubEnv
- Typ:
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
Ändert den Wert von Umgebungsvariablen in process.env
und import.meta.env
. Sie können ihren Wert durch Aufruf von vi.unstubAllEnvs
wiederherstellen.
import { vi } from 'vitest';
// `process.env.NODE_ENV` und `import.meta.env.NODE_ENV`
// sind "development" bevor "vi.stubEnv" aufgerufen wird
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;
// ändert andere Umgebungen nicht
import.meta.env.MODE === 'development';
TIP
Sie können den Wert auch einfach durch direkte Zuweisung ändern, aber Sie können vi.unstubAllEnvs
nicht verwenden, um den vorherigen Wert wiederherzustellen:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- Typ:
() => Vitest
Stellt alle import.meta.env
- und process.env
-Werte wieder her, die mit vi.stubEnv
geändert wurden. Wenn diese Methode 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`
// sind "development" vor dem Aufruf von 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();
// stellt den Wert wieder her, der vor dem ersten "stubEnv"-Aufruf gespeichert war
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Typ:
(name: string | number | symbol, value: unknown) => Vitest
Ändert den Wert von globalen Variablen. Sie können den ursprünglichen Wert durch Aufruf von vi.unstubAllGlobals
wiederherstellen.
import { vi } from 'vitest';
// `innerWidth` ist "0" vor dem Aufruf von stubGlobal
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// wenn Sie jsdom oder happy-dom verwenden
window.innerWidth === 100;
TIP
Sie können den Wert auch einfach durch direkte Zuweisung an globalThis
oder window
(wenn Sie die jsdom
- oder happy-dom
-Umgebung verwenden) ändern, aber Sie können 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
- Typ:
() => Vitest
Stellt alle globalen Werte auf globalThis
/global
(und window
/top
/self
/parent
, wenn Sie die jsdom
- oder happy-dom
-Umgebung verwenden) wieder her, die mit vi.stubGlobal
geändert wurden. Wenn diese Methode 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 ist "undefined" vor dem Aufruf von "stubGlobal"
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 ReferenceError, da dies nicht definiert ist
IntersectionObserver === undefined;
Fake-Timer
Dieser Abschnitt beschreibt, wie man mit Fake-Timern arbeitet.
vi.advanceTimersByTime
- Typ:
(ms: number) => Vitest
Diese Methode ruft jeden gestarteten Timer auf, bis die angegebene Anzahl von Millisekunden verstrichen ist oder die Warteschlange leer ist – je nachdem, was zuerst eintritt.
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 ruft jeden gestarteten Timer auf, bis die angegebene Anzahl von Millisekunden verstrichen ist oder die Warteschlange leer ist – je nachdem, was zuerst eintritt. Dabei werden auch asynchron gesetzte Timer eingeschlossen.
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
Diese Methode ruft den nächsten verfügbaren Timer auf. Nützlich, um Zusicherungen zwischen jedem Timer-Aufruf durchzuführen. Sie können diese Methode verketten, um Timer selbst zu verwalten.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Typ:
() => Promise<Vitest>
Ruft den nächsten verfügbaren Timer auf und wartet, bis dieser aufgelöst ist, falls er asynchron gesetzt wurde. Nützlich, um Zusicherungen zwischen jedem Timer-Aufruf zu machen.
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+
- Typ:
() => Vitest
Ähnlich wie vi.advanceTimersByTime
, aber diese Methode wird die Timer um die Millisekunden vorrücken, die benötigt werden, um Rückrufe auszuführen, die derzeit mit requestAnimationFrame
geplant sind.
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- Typ:
() => number
Gibt die Anzahl der wartenden Timer zurück.
vi.clearAllTimers
Diese Methode 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. Wenn das Datum nicht gemockt ist, gibt die Methode null
zurück.
vi.getRealSystemTime
- Typ:
() => number
Bei Verwendung von vi.useFakeTimers
werden Aufrufe von Date.now
gemockt. Wenn Sie die reale Zeit in Millisekunden erhalten müssen, können Sie diese Funktion aufrufen.
vi.runAllTicks
- Typ:
() => Vitest
Diese Methode ruft jede Mikroaufgabe auf, die von process.nextTick
in die Warteschlange gestellt wurde. Dabei werden auch alle Mikroaufgaben ausgeführt, die von sich selbst geplant wurden.
vi.runAllTimers
- Typ:
() => Vitest
Diese Methode ruft jeden gestarteten Timer auf, bis die Timer-Warteschlange leer ist. Das bedeutet, dass jeder Timer, der während runAllTimers
aufgerufen wird, getriggert wird. Wenn Sie ein unendliches Intervall haben, wird ein Fehler nach 10.000 Versuchen ausgelöst (kann mit fakeTimers.loopLimit
konfiguriert werden).
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 ruft jeden gestarteten Timer asynchron auf, bis die Timer-Warteschlange leer ist. Das bedeutet, dass jeder Timer, der während runAllTimersAsync
aufgerufen wird, getriggert wird, einschließlich asynchroner Timer. Wenn Sie ein unendliches Intervall haben, wird nach 10.000 Versuchen ein Fehler ausgelöst (kann mit fakeTimers.loopLimit
konfiguriert werden).
setTimeout(async () => {
console.log(await Promise.resolve('Ergebnis'));
}, 100);
await vi.runAllTimersAsync();
// log: Ergebnis
vi.runOnlyPendingTimers
- Typ:
() => Vitest
Diese Methode ruft jeden Timer auf, der nach dem Aufruf von vi.useFakeTimers
gestartet wurde. Sie löst keine Timer aus, die während ihres Aufrufs initiiert wurden.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Typ:
() => Promise<Vitest>
Diese Methode ruft jeden Timer, der nach dem Aufruf von vi.useFakeTimers
gestartet wurde, asynchron auf, einschließlich asynchroner Timer. Dabei werden keine Timer ausgelöst, die während ihres Aufrufs initiiert wurden.
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 (wirkt sich auf datumsbezogene APIs wie hrtime
, performance.now
oder new Date()
aus) – sie löst jedoch keine Timer aus. Wenn Fake-Timer nicht aktiviert sind, mockt diese Methode nur Aufrufe von Date.*
.
Nützlich, wenn Sie etwas testen müssen, das vom aktuellen Datum abhängt – zum Beispiel Aufrufe von Luxon in Ihrem Code.
Akzeptiert dieselben String- und Zahlenargumente wie der Date
-Konstruktor.
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 Mocking von Timern zu aktivieren, müssen Sie diese Methode aufrufen. Sie umschließt alle weiteren Aufrufe von Timern (wie setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
und Date
), bis vi.useRealTimers()
aufgerufen wird.
Das Mocken von nextTick
wird nicht unterstützt, wenn Vitest innerhalb von node:child_process
mit --pool=forks
ausgeführt wird. NodeJS verwendet process.nextTick
intern in node:child_process
und blockiert, wenn es gemockt wird. Das Mocken von nextTick
wird unterstützt, wenn Vitest mit --pool=threads
ausgeführt wird.
Die Implementierung basiert intern auf @sinonjs/fake-timers
.
TIP
vi.useFakeTimers()
mockt process.nextTick
und queueMicrotask
nicht automatisch. Sie können dies jedoch aktivieren, indem Sie die Option im toFake
-Argument angeben: vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })
.
vi.isFakeTimers
- Typ:
() => boolean
Gibt true
zurück, wenn Fake-Timer aktiviert sind.
vi.useRealTimers
- Typ:
() => Vitest
Wenn die Timer abgelaufen sind, können Sie diese Methode aufrufen, um gemockte Timer auf ihre ursprünglichen Implementierungen zurückzusetzen. Alle zuvor geplanten Timer werden dabei verworfen.
Verschiedenes
Eine Reihe nützlicher Hilfsfunktionen, die Vitest bereitstellt.
vi.waitFor
- 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 abgelehnter Promise zurückgegeben wird, wartet die Methode weiter, bis der Callback erfolgreich ist oder ein Timeout eintritt.
Wenn options
auf eine Zahl gesetzt ist, entspricht dies dem Setzen von { timeout: options }
.
Dies erweist sich als sehr nützlich, wenn Sie auf den Abschluss einer asynchronen Aktion warten müssen, z. B. wenn Sie einen Server starten und warten müssen, bis er gestartet ist.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Server erfolgreich gestartet wurde', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('Server ist nicht gestartet');
}
console.log('Server gestartet');
},
{
timeout: 500, // Standard ist 1000
interval: 20, // Standard ist 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 existiert im DOM', async () => {
// Beginne mit der DOM-Befüllung
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// versuchen, das Element zu finden, bis es existiert
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // Standard ist 1000
interval: 20, // Standard ist 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Wenn vi.useFakeTimers
verwendet wird, ruft vi.waitFor
automatisch vi.advanceTimersByTime(interval)
bei jedem Check-Callback auf.
vi.waitUntil
- Typ:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Dies ähnelt vi.waitFor
, aber wenn der Callback Fehler auslöst, wird die Ausführung sofort unterbrochen und eine Fehlermeldung empfangen. Wenn der Callback einen Falsy-Wert zurückgibt, wird die nächste Prüfung fortgesetzt, bis ein Truthy-Wert zurückgegeben wird. Dies erweist sich als nützlich, wenn Sie warten müssen, bis etwas existiert, bevor Sie den nächsten Schritt unternehmen.
Betrachten Sie das folgende Beispiel. Wir können vi.waitUntil
verwenden, um zu warten, bis das Element auf der Seite erscheint, und dann können wir etwas mit dem Element tun.
import { expect, test, vi } from 'vitest';
test('Element wird korrekt gerendert', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // Standard ist 1000
interval: 20, // Standard ist 50
});
// etwas mit dem Element tun
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- Typ:
<T>(factory: () => T) => T
Alle statischen import
-Anweisungen in ES-Modulen werden an den Anfang der Datei hochgezogen, sodass jeder Code, der vor den Importen definiert ist, tatsächlich nach der Auswertung der Importe ausgeführt wird.
Es kann jedoch nützlich sein, einige Nebeneffekte wie das Mocken von Daten vor dem Importieren eines Moduls auszulösen.
Um diese Einschränkung zu umgehen, können Sie statische Importe wie folgt in dynamische umschreiben:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
Beim Ausführen von Vitest können Sie dies automatisch mit der Methode vi.hoisted
erreichen. Intern konvertiert Vitest statische Importe in dynamische mit beibehaltenen Live-Bindungen.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
IMPORTE SIND NICHT VERFÜGBAR
Codeausführung vor den Importen bedeutet, dass Sie nicht auf importierte Variablen zugreifen können, da diese noch nicht definiert sind:
import { value } from './some/module.js';
vi.hoisted(() => { value }); // wirft einen Fehler
Dieser Code erzeugt einen Fehler:
Kann nicht auf '__vi_import_0__' vor der Initialisierung zugreifen
Wenn Sie auf eine Variable aus einem anderen Modul innerhalb von vi.hoisted
zugreifen müssen, verwenden Sie den dynamischen Import:
await vi.hoisted(async () => {
const { value } = await import('./some/module.js');
});
Es wird jedoch abgeraten, etwas innerhalb von vi.hoisted
zu importieren, da Importe bereits hochgezogen sind – wenn Sie etwas ausführen müssen, bevor die Tests laufen, führen Sie es einfach im importierten Modul selbst aus.
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);
Beachten Sie, dass diese Methode auch asynchron aufgerufen werden kann, selbst wenn Ihre Umgebung kein Top-Level-Await unterstützt:
const json = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- Typ:
RuntimeConfig
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),
// unterstützt das gesamte Objekt
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// unterstützt nur "sequence.hooks"
},
});
vi.resetConfig
- Typ:
RuntimeConfig
Wenn vi.setConfig
zuvor aufgerufen wurde, setzt diese Methode die Konfiguration auf den ursprünglichen Zustand zurück.