Vi
Vitest udostępnia funkcje pomocnicze za pośrednictwem swojego pomocnika vi
. Możesz uzyskać do niego dostęp globalnie (gdy włączona jest konfiguracja globalna) 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łu. 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 zaimportowane moduły z podanej ścieżki
innym modułem. Możesz używać skonfigurowanych aliasów Vite w obrębie ścieżki. Wywołanie vi.mock
jest przenoszone na górę (hoisted), więc nie ma znaczenia, gdzie je wywołasz. Zawsze zostanie wykonane przed wszystkimi importami. Jeśli potrzebujesz odwołać się do zmiennych spoza jego zakresu, możesz je zdefiniować w vi.hoisted
i użyć ich w 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ę (hoist), Vitest statycznie analizuje twoje pliki. Oznacza to, że vi
, które nie zostało bezpośrednio zaimportowane z pakietu vitest
(na przykład z jakiegoś pliku narzędziowego), nie może być wykorzystane. Użyj vi.mock
z vi
zaimportowanym z vitest
lub włącz opcję konfiguracyjną globals
.
Vitest nie będzie mockować modułów, które zostały zaimportowane w pliku konfiguracyjnym, ponieważ w momencie uruchamiania pliku testowego są już one w pamięci podręcznej. Możesz wywołać vi.resetModules()
w vi.hoisted
, aby wyczyścić wszystkie pamięci podręczne modułów przed uruchomieniem pliku testowego.
Jeśli funkcja fabrykująca (factory
) jest zdefiniowana, wszystkie importy zwracają jej wynik. Vitest wywołuje funkcję fabrykującą tylko raz i przechowuje wyniki w pamięci podręcznej dla wszystkich kolejnych importów, dopóki nie zostanie wywołane vi.unmock
lub vi.doUnmock
.
W przeciwieństwie do jest
, funkcja fabrykująca może być asynchroniczna. Możesz użyć vi.importActual
lub pomocnika z funkcją fabrykującą przekazaną jako pierwszy argument i uzyskać oryginalny moduł wewnątrz.
Możesz również podać obiekt z właściwością spy
zamiast funkcji fabrykującej. 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ż użycie obietnicy 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 odziedziczy typ. Użycie tej sygnatury wymusi również, aby typ zwracany przez funkcję fabrykującą był zgodny z oryginalnym modułem (zachowując eksporty opcjonalnie).
// @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 dedukowany
// ^?
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 używasz TypeScript z aliasami paths
skonfigurowanymi w tsconfig.json
jednak, kompilator nie będzie w stanie poprawnie rozwiązać typów importu. Aby to zadziałało, upewnij się, że zastąpisz wszystkie zaaliasowane importy ich odpowiednimi względnymi ścieżkami. Np. użyj import('./path/to/module.js')
zamiast import('@/module')
.
WARNING
vi.mock
jest przenoszone na górę pliku (hoisted). Oznacza to, że niezależnie od tego, gdzie je napiszesz (czy to w beforeEach
, czy w test
), zostanie ono faktycznie wywołane wcześniej.
Oznacza to również, że nie możesz używać żadnych zmiennych w funkcji fabrykującej, które są zdefiniowane poza funkcją fabrykującą.
Jeśli musisz użyć zmiennych w funkcji fabrykującej, spróbuj vi.doMock
. Działa tak samo, ale nie jest przenoszone na górę. Pamiętaj, że ma wpływ tylko na 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, musisz podać klucz default
w zwróconym obiekcie funkcji fabrykującej. Jest to specyficzna dla modułów ES uwaga; 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 funkcja fabrykująca nie jest dostarczona, Vitest spróbuje znaleźć plik o tej samej nazwie w podfolderze __mocks__
i użyć go jako faktycznego 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 funkcji fabrykującej lub opcji, znajdzie on plik w folderze __mocks__
do użycia jako moduł:
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 będą automatycznie mockowane. Aby odtworzyć zachowanie automockowania Jest, możesz wywołać vi.mock
dla każdego wymaganego modułu w setupFiles
.
Jeśli nie ma folderu __mocks__
ani dostarczonej funkcji fabrykującej, 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 zmockowany.
WARNING
Ta funkcja nie zmockuje modułów, które zostały zaimportowane przed jej wywołaniem. Nie zapominaj, że wszystkie statyczne importy w ESM są zawsze przenoszone na górę, więc umieszczenie tego przed statycznym importem nie spowoduje, że zostanie wywołane przed importem:
vi.doMock('./increment.js'); // to zostanie wywołane _po_ instrukcji importu
import { increment } from './increment.js';
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// moduł nie jest mockowany, ponieważ vi.doMock nie zostało jeszcze wywołane
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// możesz uzyskać dostęp do zmiennych wewnątrz funkcji fabrykującej
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('importowanie następnego modułu importuje zmockowany', async () => {
// oryginalny import NIE BYŁ MOCKOWANY, ponieważ vi.doMock jest wykonywane PO importach
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// nowy dynamiczny import zwraca zmockowany 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 typowania dla TypeScript. Po prostu zwraca przekazany mu obiekt.
Gdy partial
jest true
, oczekuje zwrócenia Partial<T>
. Domyślnie spowoduje to, że TypeScript będzie traktował tylko wartości pierwszego poziomu jako mockowane. Możesz przekazać { deep: true }
jako drugi argument, aby poinformować TypeScript, że cały obiekt jest mockowany, jeśli faktycznie tak jest.
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 równa się 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('mockowana wartość zwracana 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 zostać zmockowany. Może być przydatne, jeśli chcesz częściowo mockować moduł.
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>>
Importuje moduł ze wszystkimi jego właściwościami (w tym zagnieżdżonymi właściwościami) zmockowanymi. Przestrzega tych samych zasad, co vi.mock
. Zasady zastosowane, patrz algorytm.
vi.unmock
- Typ:
(path: string | Promise<Module>) => void
Usuwa moduł z rejestru mockowanych modułów. Wszystkie wywołania importu będą zwracać oryginalny moduł, nawet jeśli był wcześniej mockowany. To wywołanie jest przenoszone na górę pliku, więc odmockuje tylko moduły, które został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 spowoduje zaimportowanie oryginalnego modułu zamiast mocka. To nie odmockuje wcześniej zaimportowanych modułów.
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment jest już zmockowany, ponieważ vi.mock jest przeniesione na górę
increment(1) === 100;
// to jest przeniesione na górę, a funkcja fabrykująca jest wywoływana przed importem w linii 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// wszystkie wywołania są zmockowane, a `increment` zawsze zwraca 100
increment(1) === 100;
increment(30) === 100;
// to nie jest przenoszone na górę, więc inny import zwróci niemockowany moduł
vi.doUnmock('./increment.js');
// to NADAL zwraca 100, ponieważ `vi.doUnmock` nie ponownie ocenia modułu
increment(1) === 100;
increment(30) === 100;
// następny import jest niemockowany, 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 pamięć podręczną dla wszystkich modułów. Pozwala to na ponowne wykonanie modułów po ponownym zaimportowaniu. Importy najwyższego poziomu nie mogą być ponownie wykonane. Może być przydatne do izolowania modułów, gdzie lokalny stan powoduje konflikty między testami.
import { vi } from 'vitest';
import { data } from './data.js'; // Nie zostanie ponownie wykonane przed każdym testem
beforeEach(() => {
vi.resetModules();
});
test('zmień stan', async () => {
const mod = await import('./some/path.js'); // Zostanie ponownie wykonane
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 wykonane
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órego załadowanie nie możesz w inny sposób poczekać.
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 inny dynamiczny import, ta metoda będzie czekać, aż wszystkie zostaną rozwiązane.
Ta metoda będzie również czekać na następny tick setTimeout
po rozwiązaniu importu, więc wszystkie synchroniczne operacje powinny zostać zakończone do momentu 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 funkcję szpiegującą (spy) dla funkcji, choć może być zainicjowana bez niej. Za każdym razem, gdy funkcja jest wywoływana, przechowuje jej argumenty wywołania, wartości zwracane i instancje. Ponadto, możesz 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.mockObject 3.2.0+
- Typ:
<T>(value: T) => MaybeMockedDeep<T>
Głęboko mockuje właściwości i metody danego obiektu w taki sam sposób, w jaki vi.mock()
mockuje eksporty modułów. Szczegóły patrz automockowanie.
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
Sprawdza, czy dany parametr jest funkcją mockującą. Jeśli używasz TypeScript, zawęzi również jego typ.
vi.clearAllMocks
Wywołuje .mockClear()
na wszystkich szpiegach. Spowoduje to wyczyszczenie historii mocków bez wpływu na ich implementacje.
vi.resetAllMocks
Wywołuje .mockReset()
na wszystkich szpiegach. Spowoduje to wyczyszczenie historii mocków i zresetowanie implementacji każdego mocka do oryginalnej.
vi.restoreAllMocks
Wywołuje .mockRestore()
na wszystkich szpiegach. Spowoduje to wyczyszczenie historii mocków, przywrócenie wszystkich oryginalnych implementacji mocków oraz przywrócenie oryginalnych deskryptorów szpiegowanych obiektów.
vi.spyOn
- Typ:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Tworzy funkcję szpiegującą (spy) dla metody lub gettera/settera obiektu podobnie do vi.fn()
. Zwraca funkcję mockującą.
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
W środowiskach, które obsługują Explicit Resource Management, możesz użyć using
zamiast const
, aby automatycznie wywołać mockRestore
na dowolnej mockowanej funkcji po wyjściu z bloku, w którym się znajduje. Jest to szczególnie przydatne dla szpiegowanych metod:
it('wywołuje console.log', () => {
using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
debug('wiadomość')
expect(spy).toHaveBeenCalled()
})
// console.log jest przywrócone tutaj
TIP
Możesz wywołać vi.restoreAllMocks
w 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 eksportowanych metod w trybie przeglądarki. Zamiast tego możesz szpiegować każdą eksportowaną metodę, wywołując vi.mock("./file-path.js", { spy: true })
. Spowoduje to mockowanie każdego eksportu, ale zachowa jego implementację w nienaruszonym stanie, umożliwiając sprawdzenie, 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 obiektach 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 zachowa ją, dopóki unstubAllEnvs
nie zostanie wywołane 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 do wartości, które zostały zapisane 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 dla 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 zachowa ją, dopóki unstubAllGlobals
nie zostanie wywołane 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, aż upłynie określona liczba milisekund lub kolejka będzie pusta - w zależności od tego, co nastąpi wcześniej.
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, aż upłynie określona liczba milisekund lub kolejka będzie pusta - w zależności od tego, co nastąpi wcześniej. 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 sprawdzeń między każdym wywołaniem timera. Możesz wywoływać to łańcuchowo, 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 sprawdzeń 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 wywołań zwrotnych 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 zmockowaną bieżącą datę/czas. Jeśli data nie jest zmockowana, 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 wykorzystać tę funkcję.
vi.runAllTicks
- Typ:
() => Vitest
Wywołuje każde mikrozadanie, które zostało zakolejkowane za pomocą process.nextTick
. Spowoduje to również uruchomienie wszystkich mikrozadań zaplanowanych samodzielnie.
vi.runAllTimers
- Typ:
() => Vitest
Ta metoda wywoła każdy zainicjowany timer, aż kolejka timerów będzie pusta. Oznacza to, że każdy timer, który zostanie wywołany w trakcie działania runAllTimers
, zostanie uruchomiony. Jeśli masz nieskończony interwał, zgłosi błąd po 10 000 prób (można to 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, aż kolejka timerów będzie pusta. Oznacza to, że każdy timer, który zostanie wywołany w trakcie działania runAllTimersAsync
, zostanie uruchomiony, nawet asynchroniczne timery. Jeśli masz nieskończony interwał, zgłosi błąd po 10 000 prób (można to 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ł utworzony po wywołaniu vi.useFakeTimers
. Nie uruchomi żadnego timera, który został utworzony 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ł utworzony po wywołaniu vi.useFakeTimers
, nawet asynchroniczne. Nie uruchomi żadnego timera, który został utworzony 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 (będzie miało to wpływ 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 mockować tylko 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ę. Będzie ona przechwytywać wszystkie dalsze wywołania timerów (takie jak setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
i Date
), dopóki nie zostanie wywołane vi.useRealTimers()
.
Mockowanie nextTick
nie jest obsługiwane podczas uruchamiania Vitest w node:child_process
z użyciem --pool=forks
. NodeJS używa process.nextTick
wewnętrznie w node:child_process
i zawiesza działanie, 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
i queueMicrotask
. Możesz to jednak włączyć, określając opcję w argumencie toFake
: vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })
.
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ć mockowane 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 funkcji zwrotnej. Jeśli funkcja zwrotna zgłosi błąd lub zwróci odrzuconą obietnicę, będzie kontynuować czekanie, aż zakończy się sukcesem lub upłynie limit czasu.
Jeśli opcje są ustawione na liczbę, efekt jest równoważny ustawieniu { timeout: options }
.
Jest to bardzo przydatne, gdy trzeba 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 wywołań zwrotnych
// @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 () => {
// spróbuj pobrać element, aż będzie istniał
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żyto vi.useFakeTimers
, vi.waitFor
automatycznie wywołuje vi.advanceTimersByTime(interval)
w każdym wywołaniu zwrotnym sprawdzania.
vi.waitUntil
- Typ:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Jest to podobne do vi.waitFor
, ale jeśli funkcja zwrotna zgłosi jakiekolwiek błędy, wykonanie zostanie natychmiast przerwane i pojawi się komunikat o błędzie. Jeśli funkcja zwrotna zwróci wartość falsy, następne sprawdzenie będzie kontynuowane, aż zostanie zwrócona wartość truthy. Jest to przydatne, gdy trzeba poczekać, aż coś będzie istniało, zanim przejdzie się 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ś z nim zrobić.
import { expect, test, vi } from 'vitest';
test('Element renderuje się poprawnie', async () => {
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 zostanie faktycznie wykonany po wykonaniu importów.
Jednak może być przydatne wykonanie pewnych operacji z efektami ubocznymi, 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
. Pod maską Vitest przekonwertuje statyczne importy na dynamiczne z zachowaniem dynamicznych powiązań.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
IMPORTY SĄ NIEDOSTĘPNE
Uruchamianie kodu przed importami oznacza, że nie możesz uzyskać dostępu do zaimportowanych zmiennych, ponieważ nie są jeszcze zdefiniowane:
import { value } from './some/module.js';
vi.hoisted(() => { value }); // rzuca błąd
Ten kod wygeneruje błąd:
Nie można uzyskać dostępu do '__vi_import_0__' przed inicjalizacją
Jeśli potrzebujesz uzyskać dostęp do zmiennej z innego modułu wewnątrz vi.hoisted
, użyj dynamicznego importu:
await vi.hoisted(async () => {
const { value } = await import('./some/module.js');
});
Jednak nie zaleca się importowania czegokolwiek wewnątrz vi.hoisted
, ponieważ importy są już przeniesione na górę - jeśli musisz wykonać coś przed uruchomieniem testów, po prostu wykonaj to w samym zaimportowanym module.
Ta metoda zwraca wartość, która została zwrócona z funkcji fabrykującej. Możesz użyć tej wartości w swoich funkcjach fabrykujących 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 json = 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 vi.setConfig
zostało wywołane wcześniej, to zresetuje konfigurację do stanu początkowego.