Vi
Vitest poskytuje užitečné funkce prostřednictvím svého nástroje vi
. Můžete k němu přistupovat globálně (pokud je povolena konfigurace globals), nebo jej importovat přímo z vitest
:
import { vi } from 'vitest';
Mockování modulů
Tato sekce popisuje API, které můžete použít při mockování modulu. Pamatujte, že Vitest nepodporuje mockování modulů importovaných 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
Nahradí všechny importované moduly z dané cesty jiným modulem. V cestě můžete použít nakonfigurované Vite aliasy. Volání vi.mock
je vyzdviženo (hoisted), takže nezáleží na tom, kde ho zavoláte. Vždy se provede před všemi importy. Pokud potřebujete odkazovat na některé proměnné mimo jeho rozsah, můžete je definovat uvnitř vi.hoisted
a odkazovat na ně uvnitř vi.mock
.
WARNING
vi.mock
funguje pouze pro moduly, které byly importovány klíčovým slovem import
. Nefunguje s require
.
Aby bylo možné vi.mock
vyzdvihnout, Vitest staticky analyzuje vaše soubory. To znamená, že nelze použít vi
, které nebylo přímo importováno z modulu vitest
(například z nějakého pomocného souboru). Použijte vi.mock
s vi
importovaným z vitest
, nebo povolte konfigurační možnost globals
.
Vitest nebude mockovat moduly, které byly importovány uvnitř setup souboru, protože jsou v době spuštění testovacího souboru již cachované. Můžete zavolat vi.resetModules()
uvnitř vi.hoisted
pro vymazání všech mezipamětí modulů před spuštěním testovacího souboru.
Pokud je definována funkce factory
, všechny importy daného modulu vrátí její výsledek. Vitest volá factory pouze jednou a cachuje výsledky pro všechny následné importy, dokud není zavoláno vi.unmock
nebo vi.doUnmock
.
Na rozdíl od jest
může být factory asynchronní. Můžete použít vi.importActual
nebo pomocnou funkci s factory předanou jako první argument a získat původní modul uvnitř.
Od Vitest 2.1 můžete místo factory funkce poskytnout také objekt s vlastností spy
. Pokud je spy
true
, Vitest automockuje modul jako obvykle, ale nepřepíše implementaci exportů. To je užitečné, pokud chcete pouze ověřit, že exportovaná metoda byla správně volána jinou metodou.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// volá původní implementaci,
// ale umožňuje pozdější ověření chování
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Vitest také podporuje slib modulu místo řetězce v metodách vi.mock
a vi.doMock
pro lepší podporu IDE. Když je soubor přesunut, cesta se aktualizuje a importOriginal
automaticky zdědí typ. Použití této signatury také vynutí, aby návratový typ factory byl kompatibilní s původním modulem (přičemž exporty zůstanou volitelné).
// @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 je odvozen
// ^?
return {
...mod,
// nahradit některé exporty
total: vi.fn(),
};
});
Vnitřně Vitest stále pracuje s řetězcem, nikoli s objektem modulu.
Pokud však používáte TypeScript s aliasy paths
nakonfigurovanými v tsconfig.json
, kompilátor nebude schopen správně rozlišit typy importů. Aby to fungovalo, ujistěte se, že nahradíte všechny aliasované importy odpovídajícími relativními cestami. Např. použijte import('./path/to/module.js')
místo import('@/module')
.
WARNING
vi.mock
je vyzdvižen (jinými slovy, přesunut) na začátek souboru. To znamená, že kdykoli ho napíšete (ať už uvnitř beforeEach
nebo test
), bude ve skutečnosti volán předtím.
To také znamená, že uvnitř factory nemůžete použít žádné proměnné, které jsou definovány mimo její rozsah.
Pokud potřebujete použít proměnné uvnitř factory, zkuste vi.doMock
. Funguje stejně, ale není přesunut nahoru. Mějte na paměti, že mockuje pouze následné importy.
Můžete také odkazovat na proměnné definované metodou vi.hoisted
, pokud byla deklarována před 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
Pokud mockujete modul s výchozím exportem, budete muset poskytnout klíč default
v objektu vráceném factory funkcí. Toto je specifické pro moduly ES; proto se dokumentace jest
může lišit, protože jest
používá moduly CommonJS. Například,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// atd...
};
});
Pokud existuje složka __mocks__
vedle souboru, který mockujete, a factory není poskytnuta, Vitest se pokusí najít soubor se stejným názvem v podsložce __mocks__
. Tento soubor bude použit jako skutečný modul. Pokud mockujete závislost, Vitest se pokusí najít složku __mocks__
v kořenovém adresáři projektu (výchozí je process.cwd()
). Vitestu můžete sdělit, kde se závislosti nacházejí, pomocí konfigurační možnosti deps.moduleDirectories
.
Například máte tuto strukturu souborů:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Pokud zavoláte vi.mock
v testovacím souboru bez poskytnuté factory nebo možností, najde soubor ve složce __mocks__
, který použije jako modul:
// increment.test.js
import { vi } from 'vitest';
// axios je výchozí export z `__mocks__/axios.js`
import axios from 'axios';
// increment je pojmenovaný export z `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Mějte na paměti, že pokud nezavoláte vi.mock
, moduly nejsou automaticky mockovány. Chcete-li replikovat chování automockování Jest, můžete zavolat vi.mock
pro každý modul, který chcete mockovat, uvnitř setupFiles
.
Pokud neexistuje složka __mocks__
nebo není poskytnuta factory, Vitest importuje původní modul a automaticky mockuje všechny jeho exporty. Pravidla, která se použijí, naleznete v algoritmu.
vi.doMock
- Typ:
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Typ:
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Stejné jako vi.mock
, ale není přesunuto na začátek souboru, takže můžete odkazovat na proměnné v globálním rozsahu souboru. Další dynamický import modulu bude mockován.
WARNING
Toto nebude mockovat moduly, které byly importovány předtím, než bylo toto volání provedeno. Nezapomeňte, že všechny statické importní příkazy v ESM jsou vždy přesunuty nahoru, takže umístění tohoto před statický import nezajistí, že bude voláno před importem:
vi.doMock('./increment.js'); // toto bude voláno _po_ příkazu import
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// modul není mockován, protože vi.doMock ještě není voláno
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// můžete přistupovat k proměnným uvnitř factory
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('importing the next module imports mocked one', async () => {
// původní import NEBYL MOCKOVÁN, protože vi.doMock je vyhodnoceno PO importech
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// nový dynamický import vrací mockovaný modul
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>
Pomocný typ pro TypeScript. Pouze vrací objekt, který byl předán.
Pokud je partial
true
, očekává se návratová hodnota Partial<T>
. Ve výchozím nastavení to pouze způsobí, že TypeScript uvěří, že hodnoty na první úrovni jsou mockovány. Můžete předat { deep: true }
jako druhý argument, abyste TypeScriptu řekli, že celý objekt je mockován, pokud tomu tak skutečně je.
// 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 equals 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('mock return value with only partially correct typing', 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 }) // toto je chyba typu
});
vi.importActual
- Typ:
<T>(path: string) => Promise<T>
Importuje modul, obchází všechny kontroly, zda by měl být mockován. Může být užitečné, pokud chcete mockovat modul částečně.
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 modul se všemi jeho vlastnostmi (včetně vnořených vlastností) mockovanými. Řídí se stejnými pravidly jako vi.mock
. Pravidla, která se použijí, naleznete v algoritmu.
vi.unmock
- Typ:
(path: string | Promise<Module>) => void
Odstraní modul z registru mockovaných modulů. Všechna volání importu vrátí původní modul, i když byl předtím mockován. Toto volání je přesunuto na začátek souboru, takže odmockuje pouze moduly, které byly definovány například v setupFiles
.
vi.doUnmock
- Typ:
(path: string | Promise<Module>) => void
Stejné jako vi.unmock
, ale není přesunuto na začátek souboru. Další import modulu importuje původní modul místo mocku. Toto neodmockuje dříve importované moduly.
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment je již mockován, protože vi.mock je přesunuto nahoru
increment(1) === 100;
// toto je přesunuto nahoru a factory je volána před importem na řádku 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// všechna volání jsou mockována a `increment` vždy vrací 100
increment(1) === 100;
increment(30) === 100;
// toto není přesunuto nahoru, takže jiný import vrátí neodmockovaný modul
vi.doUnmock('./increment.js');
// toto STÁLE vrací 100, protože `vi.doUnmock` nepřehodnocuje modul
increment(1) === 100;
increment(30) === 100;
// další import je neodmockován, nyní `increment` je původní funkce, která vrací count + 1
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- Typ:
() => Vitest
Resetuje registr modulů vymazáním cache všech modulů. To umožňuje modulům být znovu vyhodnoceny při opětovném importu. Importy na nejvyšší úrovni nelze znovu vyhodnotit. Může být užitečné pro izolaci modulů, kde lokální stav koliduje mezi testy.
import { vi } from 'vitest';
import { data } from './data.js'; // Nebude znovu vyhodnoceno beforeEach test
beforeEach(() => {
vi.resetModules();
});
test('change state', async () => {
const mod = await import('./some/path.js'); // Bude znovu vyhodnoceno
mod.changeLocalState('new value');
expect(mod.getLocalState()).toBe('new value');
});
test('module has old state', async () => {
const mod = await import('./some/path.js'); // Bude znovu vyhodnoceno
expect(mod.getLocalState()).toBe('old value');
});
WARNING
Neresetuje registr mocků. Pro vymazání registru mocků použijte vi.unmock
nebo vi.doUnmock
.
vi.dynamicImportSettled
Počká na načtení všech importů. Užitečné, pokud máte synchronní volání, které začne importovat modul, na který jinak nemůžete čekat.
import { expect, test } from 'vitest';
// nelze sledovat import, protože Promise není vrácen
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('operations are resolved', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
Pokud během dynamického importu dojde k iniciaci dalšího dynamického importu, tato metoda počká, dokud se všechny nevyřeší.
Tato metoda také počká na další tick setTimeout
po vyřešení importu, takže všechny synchronní operace by měly být dokončeny v době, kdy se vyřeší.
Mockování funkcí a objektů
Tato sekce popisuje, jak pracovat s mocky metod a nahrazovat proměnné prostředí a globální proměnné.
vi.fn
- Typ:
(fn?: Function) => Mock
Vytvoří sledování funkce (spy), i když může být inicializován bez ní. Pokaždé, když je funkce vyvolána, ukládá své argumenty volání, návratové hodnoty a kontext (instance). Také můžete manipulovat s jejím chováním pomocí metod. Pokud není zadána žádná funkce, mock vrátí undefined
, když je vyvolán.
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
Zkontroluje, zda je daný parametr mockovací funkcí. Pokud používáte TypeScript, také zúží jeho typ.
vi.clearAllMocks
Zavolá .mockClear()
na všechny mockovací funkce. Tím se vymaže historie mocku, ale neresetuje se jeho implementace na výchozí.
vi.resetAllMocks
Zavolá .mockReset()
na všechny mockovací funkce. Tím se vymaže historie mocku a resetuje se jeho implementace na prázdnou funkci (vrátí undefined
).
vi.restoreAllMocks
Zavolá .mockRestore()
na všechny mockovací funkce. Tím se vymaže historie mocku a resetuje se jeho implementace na původní.
vi.spyOn
- Typ:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Vytvoří sledování metody nebo getter/setter objektu podobně jako vi.fn()
. Vrátí mockovací funkci.
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
Můžete zavolat vi.restoreAllMocks
uvnitř afterEach
(nebo povolit test.restoreMocks
) pro obnovení všech metod na jejich původní implementace. Tím se obnoví původní popisovač vlastnosti objektu, takže nebudete moci změnit implementaci 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()); // stále 42!
TIP
Není možné sledovat exportované metody v režimu prohlížeče. Místo toho můžete sledovat každou exportovanou metodu voláním vi.mock("./file-path.js", { spy: true })
. Tím se mockuje každý export, ale zachová se jejich původní implementace, což vám umožní ověřit, zda byla metoda správně volána.
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);
A i když je možné sledovat exporty v jsdom
nebo jiných prostředích Node.js, v budoucnu se to může změnit.
vi.stubEnv
- Typ:
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
Změní hodnotu proměnné prostředí na process.env
a import.meta.env
. Její hodnotu můžete obnovit voláním vi.unstubAllEnvs
.
import { vi } from 'vitest';
// `process.env.NODE_ENV` a `import.meta.env.NODE_ENV`
// jsou "development" před voláním "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;
// nemění jiné envs
import.meta.env.MODE === 'development';
TIP
Hodnotu můžete také změnit jednoduchým přiřazením, ale nebudete moci použít vi.unstubAllEnvs
k obnovení předchozí hodnoty:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- Typ:
() => Vitest
Obnoví všechny hodnoty import.meta.env
a process.env
, které byly změněny pomocí vi.stubEnv
. Když je voláno poprvé, Vitest si zapamatuje původní hodnotu a uloží ji, dokud není unstubAllEnvs
znovu voláno.
import { vi } from 'vitest';
// `process.env.NODE_ENV` a `import.meta.env.NODE_ENV`
// jsou "development" před voláním 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();
// obnoví na hodnotu, která byla uložena před prvním voláním "stubEnv"
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Typ:
(name: string | number | symbol, value: unknown) => Vitest
Změní hodnotu globální proměnné. Její původní hodnotu můžete obnovit voláním vi.unstubAllGlobals
.
import { vi } from 'vitest';
// `innerWidth` je "0" před voláním stubGlobal
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// pokud používáte jsdom nebo happy-dom
window.innerWidth === 100;
TIP
Hodnotu můžete také změnit jednoduchým přiřazením k globalThis
nebo window
(pokud používáte prostředí jsdom
nebo happy-dom
), ale nebudete moci použít vi.unstubAllGlobals
k obnovení původní hodnoty:
globalThis.innerWidth = 100;
// pokud používáte jsdom nebo happy-dom
window.innerWidth = 100;
vi.unstubAllGlobals
- Typ:
() => Vitest
Obnoví všechny globální hodnoty na globalThis
/global
(a window
/top
/self
/parent
, pokud používáte prostředí jsdom
nebo happy-dom
), které byly změněny pomocí vi.stubGlobal
. Když je voláno poprvé, Vitest si zapamatuje původní hodnotu a uloží ji, dokud není unstubAllGlobals
znovu voláno.
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver je "undefined" před voláním "stubGlobal"
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// pokud používáte jsdom nebo happy-dom
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// vyhodí ReferenceError, protože není definováno
IntersectionObserver === undefined;
Falešné časovače
Tato sekce popisuje, jak pracovat s falešnými časovači.
vi.advanceTimersByTime
- Typ:
(ms: number) => Vitest
Tato metoda spustí každý iniciovaný časovač, dokud neuplyne zadaný počet milisekund nebo dokud není fronta prázdná - podle toho, co nastane dříve.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- Typ:
(ms: number) => Promise<Vitest>
Tato metoda spustí každý iniciovaný časovač, dokud neuplyne zadaný počet milisekund nebo dokud není fronta prázdná - podle toho, co nastane dříve. To zahrnuje asynchronně nastavené časovače.
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
Spustí další dostupný časovač. Užitečné pro provádění ověření mezi každým spuštěním časovače. Můžete jej řetězit a spravovat časovače sami.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Typ:
() => Promise<Vitest>
Spustí další dostupný časovač a počká, dokud se nevyřeší, pokud byl nastaven asynchronně. Užitečné pro provádění ověření mezi každým spuštěním časovače.
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
Podobně jako vi.advanceTimersByTime
, ale posune časovače o milisekundy potřebné k provedení funkcí zpětného volání aktuálně naplánovaných pomocí requestAnimationFrame
.
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- Typ:
() => number
Získá počet čekajících časovačů.
vi.clearAllTimers
Odstraní všechny časovače, které jsou naplánovány ke spuštění. Tyto časovače se v budoucnu nikdy nespustí.
vi.getMockedSystemTime
- Typ:
() => Date | null
Vrátí mockované aktuální datum, které bylo nastaveno pomocí setSystemTime
. Pokud datum není mockováno, metoda vrátí null
.
vi.getRealSystemTime
- Typ:
() => number
Při použití vi.useFakeTimers
jsou volání Date.now
mockována. Pokud potřebujete získat skutečný čas v milisekundách, můžete zavolat tuto funkci.
vi.runAllTicks
- Typ:
() => Vitest
Spustí každou mikrotasku, která byla zařazena do fronty pomocí process.nextTick
. Tím se také spustí všechny mikrotasky naplánované samy sebou.
vi.runAllTimers
- Typ:
() => Vitest
Tato metoda spustí každý iniciovaný časovač, dokud není fronta časovačů prázdná. To znamená, že každý časovač naplánovaný během provádění runAllTimers
bude spuštěn. Pokud máte nekonečný interval, vyhodí chybu po 10 000 pokusech (lze konfigurovat 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>
Tato metoda asynchronně spustí každý iniciovaný časovač, dokud není fronta časovačů prázdná. To znamená, že každý časovač naplánovaný během provádění runAllTimersAsync
bude spuštěn, dokonce i asynchronní časovače. Pokud máte nekonečný interval, vyhodí chybu po 10 000 pokusech (lze konfigurovat pomocí fakeTimers.loopLimit
).
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- Typ:
() => Vitest
Tato metoda spustí každý časovač, který byl iniciován po volání vi.useFakeTimers
. Nebude spouštět žádný časovač, který byl iniciován během jejího volání.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Typ:
() => Promise<Vitest>
Tato metoda asynchronně spustí každý časovač, který byl iniciován po volání vi.useFakeTimers
, dokonce i asynchronní. Nebude spouštět žádný časovač, který byl iniciován během jejího volání.
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
Pokud jsou povoleny falešné časovače, tato metoda simuluje změnu systémových hodin uživatelem. Ovlivní API související s datem, jako je hrtime
, performance.now
nebo new Date()
, ale nespustí žádné časovače. Pokud falešné časovače nejsou povoleny, tato metoda pouze mockuje volání Date.*
.
Užitečné, pokud potřebujete testovat cokoli, co závisí na aktuálním datu - například volání Luxon ve vašem kódu.
Přijímá stejné argumenty typu string a number jako 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
Pro povolení mockování časovačů musíte zavolat tuto metodu. Přesměruje všechna další volání časovačů (jako setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
a Date
), dokud není zavoláno vi.useRealTimers()
.
Mockování nextTick
není podporováno při spuštění Vitest uvnitř node:child_process
pomocí --pool=forks
. NodeJS interně používá process.nextTick
v node:child_process
a zablokuje se, když je mockován. Mockování nextTick
je podporováno při spuštění Vitest s --pool=threads
.
Implementace je interně založena na @sinonjs/fake-timers
.
TIP
vi.useFakeTimers()
automaticky nemockuje process.nextTick
. Ale můžete to povolit zadáním možnosti v argumentu toFake
: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers
- Typ:
() => boolean
Vrátí true
, pokud jsou povoleny falešné časovače.
vi.useRealTimers
- Typ:
() => Vitest
Když časovače doběhnou, můžete zavolat tuto metodu pro vrácení mockovaných časovačů na jejich původní implementace. Všechny časovače, které byly naplánovány dříve, budou zrušeny.
Různé
Sada užitečných funkcí, které Vitest poskytuje.
vi.waitFor
- Typ:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Počká na úspěšné provedení zpětného volání. Pokud zpětné volání vyhodí chybu nebo vrátí zamítnutý slib, bude pokračovat v čekání, dokud nebude úspěšné nebo nevyprší časový limit.
To je velmi užitečné, když potřebujete počkat na dokončení nějaké asynchronní akce, například když spustíte server a potřebujete počkat, než se spustí.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Server started successfully', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('Server not started');
}
console.log('Server started');
},
{
timeout: 500, // výchozí je 1000
interval: 20, // výchozí je 50
}
);
expect(server.isReady).toBe(true);
});
Funguje také s asynchronními zpětnými voláními
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('Element exists in a DOM', async () => {
// začít plnit DOM
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// zkusit získat element, dokud neexistuje
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // výchozí je 1000
interval: 20, // výchozí je 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Pokud je použito vi.useFakeTimers
, vi.waitFor
automaticky volá vi.advanceTimersByTime(interval)
při každém zpětném volání kontroly.
vi.waitUntil
- Typ:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Toto funguje podobně jako vi.waitFor
, ale pokud zpětné volání vyhodí jakékoli chyby, provádění je okamžitě přerušeno a je přijata chybová zpráva. Pokud zpětné volání vrátí nepravdivou hodnotu, další kontrola bude pokračovat, dokud nebude vrácena pravdivá hodnota. To je užitečné, když potřebujete počkat, než něco existuje, než podniknete další krok.
Podívejte se na příklad níže. Můžeme použít vi.waitUntil
k čekání na zobrazení prvku na stránce a poté s prvkem něco udělat.
import { expect, test, vi } from 'vitest';
test('Element render correctly', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // výchozí je 1000
interval: 20, // výchozí je 50
});
// něco udělat s prvkem
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- Typ:
<T>(factory: () => T) => T
Všechny statické importní příkazy v modulech ES jsou přesunuty na začátek souboru, takže jakýkoli kód, který je definován před importy, bude ve skutečnosti proveden po vyhodnocení importů.
Nicméně může být užitečné provést některé vedlejší efekty, například mockování dat, před importem modulu.
Chcete-li toto omezení obejít, můžete přepsat statické importy na dynamické takto:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
Při spuštění vitest
to můžete provést automaticky pomocí metody vi.hoisted
.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
Tato metoda vrací hodnotu, která byla vrácena z factory. Tuto hodnotu můžete použít ve svých vi.mock
factories, pokud potřebujete snadný přístup k lokálně definovaným proměnným:
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);
Všimněte si, že tuto metodu lze volat i asynchronně, i když vaše prostředí nepodporuje 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 konfiguraci pro aktuální testovací soubor. Tato metoda podporuje pouze konfigurační možnosti, které ovlivní aktuální testovací soubor:
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// podporuje celý objekt
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// podporuje pouze "sequence.hooks"
},
});
vi.resetConfig
- Typ:
RuntimeConfig
Pokud bylo předtím voláno vi.setConfig
, tato metoda resetuje konfiguraci na původní stav.