Vi
Vitest udostępnia funkcje pomocnicze, które ułatwiają korzystanie z pomocnika vi
. Możesz uzyskać do niego dostęp globalnie (gdy włączona jest konfiguracja globals) lub zaimportować go bezpośrednio z vitest
:
import { vi } from 'vitest';
Mockowanie modułów
Ta sekcja opisuje API, którego możesz używać podczas mockowania modułów. Pamiętaj, że Vitest nie obsługuje mockowania modułów importowanych za pomocą require()
.
vi.mock
- Typ:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Typ:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Zastępuje wszystkie moduły importowane z podanej ścieżki path
innym modułem. Możesz używać skonfigurowanych aliasów Vite w ścieżce. Wywołanie vi.mock
jest przenoszone na górę (hoisted), więc nie ma znaczenia, gdzie je umieścisz. Zawsze zostanie wykonane przed wszystkimi importami. Jeśli potrzebujesz odwołać się do zmiennych spoza jego zakresu, możesz zdefiniować je wewnątrz vi.hoisted
i odwołać się do nich wewnątrz vi.mock
.
WARNING
vi.mock
działa tylko dla modułów, które zostały zaimportowane za pomocą słowa kluczowego import
. Nie działa z require
.
Aby przenieść vi.mock
na górę, Vitest statycznie analizuje pliki. Oznacza to, że vi
, które nie zostało bezpośrednio zaimportowane z pakietu vitest
(na przykład z pliku pomocniczego), nie może być użyte. Użyj vi.mock
z vi
zaimportowanym z vitest
lub włącz opcję konfiguracyjną globals
.
Vitest nie będzie mockować modułów zaimportowanych wewnątrz pliku setup, ponieważ są one buforowane w momencie uruchomienia pliku testowego. Możesz wywołać vi.resetModules()
wewnątrz vi.hoisted
, aby wyczyścić bufory modułów przed uruchomieniem pliku testowego.
Jeśli funkcja factory
jest zdefiniowana, wszystkie importy zwrócą jej wynik. Vitest wywołuje factory tylko raz i buforuje wyniki dla wszystkich kolejnych importów, dopóki nie zostanie wywołane vi.unmock
lub vi.doUnmock
.
W przeciwieństwie do jest
, factory może być asynchroniczne. Możesz użyć vi.importActual
lub pomocnika z factory przekazanym jako pierwszy argument, aby uzyskać oryginalny moduł wewnątrz.
Od Vitest 2.1, możesz również podać obiekt z właściwością spy
zamiast funkcji factory. Jeśli spy
jest true
, Vitest automatycznie mockuje moduł jak zwykle, ale nie nadpisuje implementacji eksportów. Jest to przydatne, jeśli chcesz tylko sprawdzić, czy wyeksportowana metoda została poprawnie wywołana przez inną metodę.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// wywołuje oryginalną implementację,
// ale pozwala na późniejsze sprawdzenie zachowania
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Vitest obsługuje również obietnicę modułu zamiast ciągu znaków w metodach vi.mock
i vi.doMock
dla lepszego wsparcia IDE. Gdy plik zostanie przeniesiony, ścieżka zostanie zaktualizowana, a importOriginal
automatycznie dziedziczy typ. Użycie tej sygnatury wymusi również zgodność typu zwracanego przez factory z oryginalnym modułem (zachowując opcjonalność eksportów).
// @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 jest wnioskowany
// ^?
return {
...mod,
// zastąp niektóre eksporty
total: vi.fn(),
};
});
Pod maską Vitest nadal operuje na ciągu znaków, a nie na obiekcie modułu.
Jeśli jednak używasz TypeScript z aliasami paths
skonfigurowanymi w tsconfig.json
, kompilator nie będzie w stanie poprawnie rozwiązać typów importu. Aby to zadziałało, upewnij się, że zastąpisz wszystkie aliasowane importy odpowiadającymi im ścieżkami względnymi. Np. użyj import('./path/to/module.js')
zamiast import('@/module')
.
WARNING
vi.mock
jest przenoszone (innymi słowy, hoisted) na górę pliku. Oznacza to, że niezależnie od tego, gdzie je umieścisz (czy to wewnątrz beforeEach
, czy test
), faktycznie zostanie wykonane przed tym.
Oznacza to również, że nie możesz używać zmiennych wewnątrz factory, które są zdefiniowane poza factory.
Jeśli potrzebujesz użyć zmiennych wewnątrz factory, spróbuj vi.doMock
. Działa tak samo, ale nie jest przenoszone na górę. Pamiętaj, że mockuje tylko kolejne importy.
Możesz również odwołać się do zmiennych zdefiniowanych przez metodę vi.hoisted
, jeśli została zadeklarowana przed 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
Jeśli mockujesz moduł z domyślnym eksportem, będziesz musiał podać klucz default
w obiekcie zwracanym przez funkcję factory. Jest to specyfika modułów ES; dlatego dokumentacja jest
może się różnić, ponieważ jest
używa modułów CommonJS. Na przykład,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// itd...
};
});
Jeśli obok mockowanego pliku znajduje się folder __mocks__
, a factory nie jest podane, Vitest spróbuje znaleźć plik o tej samej nazwie w podfolderze __mocks__
i użyć go jako rzeczywistego modułu. Jeśli mockujesz zależność, Vitest spróbuje znaleźć folder __mocks__
w katalogu głównym projektu (domyślnie process.cwd()
). Możesz wskazać Vitest, gdzie znajdują się zależności, za pomocą opcji konfiguracyjnej deps.moduleDirectories
.
Na przykład, masz taką strukturę plików:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Jeśli wywołasz vi.mock
w pliku testowym bez podania factory lub opcji, znajdzie plik w folderze __mocks__
, który zostanie użyty jako moduł:
// increment.test.js
import { vi } from 'vitest';
// axios to domyślny eksport z `__mocks__/axios.js`
import axios from 'axios';
// increment to nazwany eksport z `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Pamiętaj, że jeśli nie wywołasz vi.mock
, moduły nie są mockowane automatycznie. Aby odtworzyć zachowanie automatycznego mockowania Jest, możesz wywołać vi.mock
dla każdego wymaganego modułu wewnątrz setupFiles
.
Jeśli nie ma folderu __mocks__
ani podanego factory, Vitest zaimportuje oryginalny moduł i automatycznie mockuje wszystkie jego eksporty. Zasady zastosowane, patrz algorytm.
vi.doMock
- Typ:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Typ:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
To samo co vi.mock
, ale nie jest przenoszone na górę pliku, więc możesz odwoływać się do zmiennych w globalnym zakresie pliku. Następny dynamiczny import modułu zostanie zamockowany.
WARNING
To nie zamockuje modułów zaimportowanych przed wywołaniem tej funkcji. Nie zapominaj, że wszystkie statyczne importy w ESM są zawsze przenoszone na górę, więc umieszczenie tego przed statycznym importem nie wymusi jego wykonania przed importem:
vi.doMock('./increment.js'); // to zostanie wykonane _po_ instrukcji importu
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// moduł nie jest zamockowany, ponieważ vi.doMock nie zostało jeszcze wykonane
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// możesz uzyskać dostęp do zmiennych wewnątrz factory
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('importowanie następnego modułu importuje zamockowany', async () => {
// oryginalny import NIE BYŁ ZAMOCKOWANY, ponieważ vi.doMock jest ewaluowane PO importach
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// nowy dynamiczny import zwraca zamockowany moduł
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>
Pomocnik typu dla TypeScript. Po prostu zwraca przekazany obiekt.
Gdy partial
jest true
, oczekuje wartości zwracanej typu Partial<T>
. Domyślnie, to tylko sprawi, że TypeScript uwierzy, że wartości na pierwszym poziomie są zamockowane. Możesz przekazać { deep: true }
jako drugi argument, aby poinformować TypeScript, że cały obiekt jest zamockowany, jeśli faktycznie tak jest.
// 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 równa się 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('mock zwraca wartość z tylko częściowo poprawnym typowaniem', 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 }) // to jest błąd typu
});
vi.importActual
- Typ:
<T>(path: string) => Promise<T>
Importuje moduł, omijając wszystkie sprawdzenia, czy powinien być zamockowany. Może być przydatne, jeśli chcesz częściowo zamockować moduł.
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>>
Importuje moduł ze wszystkimi jego właściwościami (w tym zagnieżdżonymi) zamockowanymi. Przestrzega tych samych zasad co vi.mock
. Zasady zastosowane, patrz algorytm.
vi.unmock
- Typ:
(path: string | Promise<Module>) => void
Usuwa moduł z rejestru zamockowanych. Wszystkie wywołania importu zwrócą oryginalny moduł, nawet jeśli był wcześniej zamockowany. To wywołanie jest przenoszone na górę pliku, więc odmockuje tylko moduły zdefiniowane na przykład w setupFiles
.
vi.doUnmock
- Typ:
(path: string | Promise<Module>) => void
To samo co vi.unmock
, ale nie jest przenoszone na górę pliku. Następny import modułu zaimportuje oryginalny moduł zamiast mocka. To nie odmockuje wcześniej zaimportowanych modułów.
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment jest już zamockowany, ponieważ vi.mock jest przenoszone na górę
increment(1) === 100;
// to jest przenoszone na górę, a factory jest wywoływane przed importem w linii 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// wszystkie wywołania są zamockowane, a `increment` zawsze zwraca 100
increment(1) === 100;
increment(30) === 100;
// to nie jest przenoszone na górę, więc inny import zwróci odmockowany moduł
vi.doUnmock('./increment.js');
// to NADAL zwraca 100, ponieważ `vi.doUnmock` nie reewaluuje modułu
increment(1) === 100;
increment(30) === 100;
// następny import jest odmockowany, teraz `increment` to oryginalna funkcja, która zwraca count + 1
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- Typ:
() => Vitest
Resetuje rejestr modułów, czyszcząc bufor wszystkich modułów. Pozwala to na ponowną ewaluację modułów podczas ponownego importu. Importy najwyższego poziomu nie mogą być ponownie ewaluowane. Może być przydatne do izolowania modułów, w których lokalny stan koliduje między testami.
import { vi } from 'vitest';
import { data } from './data.js'; // Nie zostanie ponownie ewaluowane przed każdym testem
beforeEach(() => {
vi.resetModules();
});
test('zmień stan', async () => {
const mod = await import('./some/path.js'); // Zostanie ponownie ewaluowane
mod.changeLocalState('nowa wartość');
expect(mod.getLocalState()).toBe('nowa wartość');
});
test('moduł ma stary stan', async () => {
const mod = await import('./some/path.js'); // Zostanie ponownie ewaluowane
expect(mod.getLocalState()).toBe('stara wartość');
});
WARNING
Nie resetuje rejestru mocków. Aby wyczyścić rejestr mocków, użyj vi.unmock
lub vi.doUnmock
.
vi.dynamicImportSettled
Czeka na załadowanie wszystkich importów. Przydatne, jeśli masz synchroniczne wywołanie, które rozpoczyna importowanie modułu, na które w inny sposób nie możesz czekać.
import { expect, test } from 'vitest';
// nie można śledzić importu, ponieważ Promise nie jest zwracany
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('operacje są rozwiązane', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
Jeśli podczas dynamicznego importu zostanie zainicjowany kolejny dynamiczny import, ta metoda poczeka, aż wszystkie zostaną rozwiązane.
Ta metoda poczeka również na następny tick setTimeout
po rozwiązaniu importu, więc wszystkie synchroniczne operacje powinny zostać zakończone w momencie jej rozwiązania.
Mockowanie funkcji i obiektów
Ta sekcja opisuje, jak pracować z mockami metod i zastępować zmienne środowiskowe i globalne.
vi.fn
- Typ:
(fn?: Function) => Mock
Tworzy szpiega na funkcji, choć można go zainicjować bez niej. Za każdym razem, gdy funkcja jest wywoływana, przechowuje jej argumenty wywołania, zwracane wartości i instancje. Możesz również manipulować jej zachowaniem za pomocą metod. Jeśli nie podano funkcji, mock zwróci undefined
po wywołaniu.
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
Sprawdza, czy dany parametr jest funkcją mock. Jeśli używasz TypeScript, zawęzi również jej typ.
vi.clearAllMocks
Wywoła .mockClear()
na wszystkich szpiegach. Spowoduje to wyczyszczenie historii mocków, ale nie zresetuje ich implementacji do domyślnej.
vi.resetAllMocks
Wywoła .mockReset()
na wszystkich szpiegach. Spowoduje to wyczyszczenie historii mocków i zresetowanie ich implementacji do pustej funkcji (zwróci undefined
).
vi.restoreAllMocks
Wywoła .mockRestore()
na wszystkich szpiegach. Spowoduje to wyczyszczenie historii mocków i zresetowanie ich implementacji do oryginalnej.
vi.spyOn
- Typ:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Tworzy szpiega na metodzie lub getterze/setterze obiektu podobnie do vi.fn()
. Zwraca funkcję 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
Możesz wywołać vi.restoreAllMocks
wewnątrz afterEach
(lub włączyć test.restoreMocks
), aby przywrócić wszystkie metody do ich oryginalnych implementacji. Spowoduje to przywrócenie oryginalnego deskryptora obiektu, więc nie będziesz mógł zmienić implementacji metody:
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()); // nadal 42!
TIP
Nie jest możliwe szpiegowanie wyeksportowanych metod w trybie przeglądarki. Zamiast tego możesz szpiegować każdą wyeksportowaną metodę, wywołując vi.mock("./file-path.js", { spy: true })
. Spowoduje to zamockowanie każdego eksportu, ale zachowa jego implementację w nienaruszonym stanie, co pozwoli sprawdzić, czy metoda została poprawnie wywołana.
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);
I chociaż możliwe jest szpiegowanie eksportów w środowiskach jsdom
lub innych środowiskach Node.js, może się to zmienić w przyszłości.
vi.stubEnv
- Typ:
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
Zmienia wartość zmiennej środowiskowej w process.env
i import.meta.env
. Możesz przywrócić jej wartość, wywołując vi.unstubAllEnvs
.
import { vi } from 'vitest';
// `process.env.NODE_ENV` i `import.meta.env.NODE_ENV`
// są "development" przed wywołaniem "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;
// nie zmienia innych zmiennych środowiskowych
import.meta.env.MODE === 'development';
TIP
Możesz również zmienić wartość, po prostu ją przypisując, ale nie będziesz mógł użyć vi.unstubAllEnvs
, aby przywrócić poprzednią wartość:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- Typ:
() => Vitest
Przywraca wszystkie wartości import.meta.env
i process.env
, które zostały zmienione za pomocą vi.stubEnv
. Gdy zostanie wywołana po raz pierwszy, Vitest zapamiętuje oryginalną wartość i będzie ją przechowywać, dopóki unstubAllEnvs
nie zostanie wywołana ponownie.
import { vi } from 'vitest';
// `process.env.NODE_ENV` i `import.meta.env.NODE_ENV`
// są "development" przed wywołaniem 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();
// przywraca wartość, która była przechowywana przed pierwszym wywołaniem "stubEnv"
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Typ:
(name: string | number | symbol, value: unknown) => Vitest
Zmienia wartość zmiennej globalnej. Możesz przywrócić jej oryginalną wartość, wywołując vi.unstubAllGlobals
.
import { vi } from 'vitest';
// `innerWidth` jest "0" przed wywołaniem stubGlobal
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// jeśli używasz jsdom lub happy-dom
window.innerWidth === 100;
TIP
Możesz również zmienić wartość, po prostu przypisując ją do globalThis
lub window
(jeśli używasz środowiska jsdom
lub happy-dom
), ale nie będziesz mógł użyć vi.unstubAllGlobals
, aby przywrócić oryginalną wartość:
globalThis.innerWidth = 100;
// jeśli używasz jsdom lub happy-dom
window.innerWidth = 100;
vi.unstubAllGlobals
- Typ:
() => Vitest
Przywraca wszystkie wartości globalne w globalThis
/global
(i window
/top
/self
/parent
, jeśli używasz środowiska jsdom
lub happy-dom
), które zostały zmienione za pomocą vi.stubGlobal
. Gdy zostanie wywołana po raz pierwszy, Vitest zapamiętuje oryginalną wartość i będzie ją przechowywać, dopóki unstubAllGlobals
nie zostanie wywołana ponownie.
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver jest "undefined" przed wywołaniem "stubGlobal"
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// jeśli używasz jsdom lub happy-dom
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// rzuca ReferenceError, ponieważ nie jest zdefiniowane
IntersectionObserver === undefined;
Fałszywe timery
Ta sekcja opisuje, jak pracować z fałszywymi timerami.
vi.advanceTimersByTime
- Typ:
(ms: number) => Vitest
Ta metoda wywoła każdy zainicjowany timer, dopóki nie upłynie określona liczba milisekund lub kolejka nie będzie pusta - cokolwiek nastąpi pierwsze.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- Typ:
(ms: number) => Promise<Vitest>
Ta metoda wywoła każdy zainicjowany timer, dopóki nie upłynie określona liczba milisekund lub kolejka nie będzie pusta - cokolwiek nastąpi pierwsze. Będzie to obejmować asynchronicznie ustawione timery.
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
Wywoła następny dostępny timer. Przydatne do wykonywania asercji między każdym wywołaniem timera. Możesz wywoływać go w łańcuchu, aby samodzielnie zarządzać timerami.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Typ:
() => Promise<Vitest>
Wywoła następny dostępny timer i poczeka, aż zostanie rozwiązany, jeśli został ustawiony asynchronicznie. Przydatne do wykonywania asercji między każdym wywołaniem timera.
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
Podobnie do vi.advanceTimersByTime
, ale przesunie timery o milisekundy potrzebne do wykonania callbacków aktualnie zaplanowanych za pomocą requestAnimationFrame
.
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- Typ:
() => number
Pobiera liczbę oczekujących timerów.
vi.clearAllTimers
Usuwa wszystkie timery, które są zaplanowane do uruchomienia. Te timery nigdy nie zostaną uruchomione w przyszłości.
vi.getMockedSystemTime
- Typ:
() => Date | null
Zwraca zamockowaną bieżącą datę, która została ustawiona za pomocą setSystemTime
. Jeśli data nie jest zamockowana, metoda zwróci null
.
vi.getRealSystemTime
- Typ:
() => number
Podczas używania vi.useFakeTimers
, wywołania Date.now
są mockowane. Jeśli potrzebujesz uzyskać rzeczywisty czas w milisekundach, możesz wywołać tę funkcję.
vi.runAllTicks
- Typ:
() => Vitest
Wywołuje każde mikrozadanie, które zostało zakolejkowane przez process.nextTick
. Spowoduje to również uruchomienie wszystkich mikrozadań zaplanowanych przez nie same.
vi.runAllTimers
- Typ:
() => Vitest
Ta metoda wywoła każdy zainicjowany timer, dopóki kolejka timerów nie będzie pusta. Oznacza to, że każdy timer wywołany podczas runAllTimers
zostanie uruchomiony. Jeśli masz nieskończony interwał, rzuci wyjątek po 10 000 próbach (można skonfigurować za pomocą 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
- Typ:
() => Promise<Vitest>
Ta metoda asynchronicznie wywoła każdy zainicjowany timer, dopóki kolejka timerów nie będzie pusta. Oznacza to, że każdy timer wywołany podczas runAllTimersAsync
zostanie uruchomiony, nawet asynchroniczne timery. Jeśli masz nieskończony interwał, rzuci wyjątek po 10 000 próbach (można skonfigurować za pomocą fakeTimers.loopLimit
).
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- Typ:
() => Vitest
Ta metoda wywoła każdy timer, który został zainicjowany po wywołaniu vi.useFakeTimers
. Nie uruchomi żadnego timera, który został zainicjowany podczas jej wywołania.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Typ:
() => Promise<Vitest>
Ta metoda asynchronicznie wywoła każdy timer, który został zainicjowany po wywołaniu vi.useFakeTimers
, nawet asynchroniczne. Nie uruchomi żadnego timera, który został zainicjowany podczas jej wywołania.
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
Jeśli włączone są fałszywe timery, ta metoda symuluje zmianę zegara systemowego przez użytkownika (wpłynie na API związane z datą, takie jak hrtime
, performance.now
lub new Date()
) - jednak nie uruchomi żadnych timerów. Jeśli fałszywe timery nie są włączone, ta metoda będzie tylko mockować wywołania Date.*
.
Przydatne, jeśli musisz przetestować coś, co zależy od bieżącej daty - na przykład wywołania Luxon w twoim kodzie.
Akceptuje te same argumenty typu string i number co Date
.
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
Aby włączyć mockowanie timerów, musisz wywołać tę metodę. Zawiśnie ona wszystkie dalsze wywołania timerów (takie jak setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
, i Date
) do momentu wywołania vi.useRealTimers()
.
Mockowanie nextTick
nie jest obsługiwane podczas uruchamiania Vitest wewnątrz node:child_process
za pomocą --pool=forks
. NodeJS wewnętrznie używa process.nextTick
w node:child_process
i zawiesza się, gdy jest mockowane. Mockowanie nextTick
jest obsługiwane podczas uruchamiania Vitest z --pool=threads
.
Implementacja opiera się wewnętrznie na @sinonjs/fake-timers
.
TIP
vi.useFakeTimers()
nie mockuje automatycznie process.nextTick
. Możesz to włączyć, określając opcję w argumencie toFake
: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers
- Typ:
() => boolean
Zwraca true
, jeśli włączone są fałszywe timery.
vi.useRealTimers
- Typ:
() => Vitest
Gdy timery się wyczerpią, możesz wywołać tę metodę, aby przywrócić zamockowane timery do ich oryginalnych implementacji. Wszystkie timery, które zostały zaplanowane wcześniej, zostaną odrzucone.
Różne
Zestaw przydatnych funkcji pomocniczych, które udostępnia Vitest.
vi.waitFor
- Typ:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Czeka na pomyślne wykonanie callbacku. Jeśli callback rzuci błąd lub zwróci odrzuconą obietnicę, będzie kontynuował czekanie, aż się powiedzie lub upłynie limit czasu.
Jest to bardzo przydatne, gdy musisz poczekać na zakończenie jakiejś asynchronicznej akcji, na przykład gdy uruchamiasz serwer i musisz poczekać, aż się uruchomi.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Serwer uruchomiony pomyślnie', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('Serwer nie uruchomiony');
}
console.log('Serwer uruchomiony');
},
{
timeout: 500, // domyślnie 1000
interval: 20, // domyślnie 50
}
);
expect(server.isReady).toBe(true);
});
Działa również dla asynchronicznych callbacków
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('Element istnieje w DOM', async () => {
// rozpocznij wypełnianie DOM
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// próbuj pobrać element, dopóki nie istnieje
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // domyślnie 1000
interval: 20, // domyślnie 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Jeśli używane jest vi.useFakeTimers
, vi.waitFor
automatycznie wywołuje vi.advanceTimersByTime(interval)
w każdym callbacku sprawdzającym.
vi.waitUntil
- Typ:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Jest to podobne do vi.waitFor
, ale jeśli callback rzuci jakiekolwiek błędy, wykonanie jest natychmiast przerywane i otrzymywany jest komunikat o błędzie. Jeśli callback zwróci wartość falsy, następne sprawdzenie będzie kontynuowane, dopóki nie zostanie zwrócona wartość truthy. Jest to przydatne, gdy musisz poczekać, aż coś będzie istnieć, zanim przejdziesz do następnego kroku.
Spójrz na poniższy przykład. Możemy użyć vi.waitUntil
, aby poczekać, aż element pojawi się na stronie, a następnie możemy coś zrobić z tym elementem.
import { expect, test, vi } from 'vitest';
test('Element renderuje poprawnie', async ()l => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // domyślnie 1000
interval: 20, // domyślnie 50
});
// zrób coś z elementem
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- Typ:
<T>(factory: () => T) => T
Wszystkie statyczne instrukcje import
w modułach ES są przenoszone na górę pliku, więc każdy kod zdefiniowany przed importami faktycznie zostanie wykonany po ewaluacji importów.
Jednak może być przydatne wywołanie niektórych efektów ubocznych, takich jak mockowanie dat przed zaimportowaniem modułu.
Aby ominąć to ograniczenie, możesz przepisać statyczne importy na dynamiczne w ten sposób:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js'
Podczas uruchamiania vitest
, możesz to zrobić automatycznie, używając metody vi.hoisted
.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
Ta metoda zwraca wartość, która została zwrócona przez factory. Możesz użyć tej wartości w swoich factory vi.mock
, jeśli potrzebujesz łatwego dostępu do lokalnie zdefiniowanych zmiennych:
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);
Zauważ, że ta metoda może być również wywoływana asynchronicznie, nawet jeśli twoje środowisko nie obsługuje top-level await:
const promised = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- Typ:
RuntimeConfig
Aktualizuje konfigurację dla bieżącego pliku testowego. Ta metoda obsługuje tylko opcje konfiguracyjne, które wpłyną na bieżący plik testowy:
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// obsługuje cały obiekt
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// obsługuje tylko "sequence.hooks"
},
});
vi.resetConfig
- Typ:
RuntimeConfig
Jeśli wcześniej wywołano vi.setConfig
, spowoduje to zresetowanie konfiguracji do pierwotnego stanu.