vi
Vitest met à votre disposition des fonctions utilitaires via son module vi
. Vous pouvez y accéder globalement (si la configuration globale est activée) ou l'importer directement depuis vitest
:
import { vi } from 'vitest';
Mocking de modules
Cette section décrit l'API à utiliser pour le mocking de modules. Notez que Vitest ne prend pas en charge le mocking des modules importés avec require()
.
vi.mock
- Type :
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Type :
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Substitue tous les modules importés depuis le path
fourni par un autre module. Vous pouvez utiliser les alias Vite configurés dans un chemin. L'appel à vi.mock
est hoisté, ce qui signifie qu'il sera toujours exécuté avant toutes les importations, quel que soit l'endroit où vous l'appelez. Si vous avez besoin de référencer des variables en dehors de sa portée, vous pouvez les définir dans vi.hoisted
et les référencer dans vi.mock
.
WARNING
vi.mock
ne fonctionne que pour les modules importés avec le mot-clé import
. Il ne fonctionne pas avec require
.
Afin de hoister vi.mock
, Vitest analyse statiquement vos fichiers. Cela implique que vi
ne peut pas être utilisé s'il n'est pas directement importé du package vitest
(par exemple, depuis un fichier utilitaire). Utilisez vi.mock
avec vi
importé de vitest
, ou activez l'option de configuration globals
.
Vitest ne mockera pas les modules importés dans un fichier de setup car ils sont mis en cache au moment de l'exécution d'un fichier de test. Vous pouvez appeler vi.resetModules()
dans vi.hoisted
pour effacer tous les caches de modules avant d'exécuter un fichier de test.
Si la fonction factory
est définie, toutes les importations renverront son résultat. Vitest n'appelle la factory qu'une seule fois et met en cache les résultats pour toutes les importations ultérieures jusqu'à ce que vi.unmock
ou vi.doUnmock
soit appelé.
Contrairement à jest
, la factory peut être asynchrone. Vous pouvez utiliser vi.importActual
ou un utilitaire avec la factory passée en premier argument, et obtenir le module original à l'intérieur.
Vous pouvez également fournir un objet avec une propriété spy
au lieu d'une fonction factory. Si spy
est true
, Vitest mockera automatiquement le module comme d'habitude, mais il ne remplacera pas l'implémentation des exportations. Ceci est utile si vous voulez simplement affirmer que la méthode exportée a été appelée correctement par une autre méthode.
import { calculator } from './src/calculator.ts';
vi.mock('./src/calculator.ts', { spy: true });
// appelle l'implémentation originale,
// mais permet d'affirmer le comportement plus tard
const result = calculator(1, 2);
expect(result).toBe(3);
expect(calculator).toHaveBeenCalledWith(1, 2);
expect(calculator).toHaveReturned(3);
Vitest prend également en charge une promesse de module au lieu d'une chaîne dans les méthodes vi.mock
et vi.doMock
pour un meilleur support IDE. Lorsque le fichier est déplacé, le chemin sera mis à jour, et importOriginal
hérite automatiquement du type. L'utilisation de cette signature forcera également le type de retour de la factory à être compatible avec le module original (tout en permettant que les exportations soient facultatives).
// @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(); // le type est inféré
// ^?
return {
...mod,
// remplacer certaines exportations
total: vi.fn(),
};
});
En interne, Vitest opère toujours sur une chaîne et non sur un objet module.
Si vous utilisez TypeScript avec des alias paths
configurés dans tsconfig.json
, le compilateur ne pourra cependant pas résoudre correctement les types d'importation. Pour que cela fonctionne, assurez-vous de remplacer toutes les importations aliasées par leurs chemins relatifs correspondants. Par exemple, utilisez import('./path/to/module.js')
au lieu de import('@/module')
.
WARNING
vi.mock
est hoisté (en d'autres termes, déplacé) en haut du fichier. Cela signifie que, peu importe où vous l'écrivez (que ce soit dans beforeEach
ou test
), il sera en fait appelé avant l'exécution de ce bloc.
Cela signifie également que vous ne pouvez pas utiliser de variables à l'intérieur de la factory qui sont définies en dehors de la factory.
Si vous avez besoin d'utiliser des variables à l'intérieur de la factory, essayez vi.doMock
. Il fonctionne de la même manière mais n'est pas hoisté. Notez qu'il ne mocke que les importations ultérieures.
Vous pouvez également référencer des variables définies par la méthode vi.hoisted
si elle a été déclarée avant 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
Si vous mockez un module avec une exportation par défaut, vous devrez fournir une clé default
dans l'objet de la fonction factory retournée. C'est une particularité des modules ES ; par conséquent, la documentation jest
peut différer car jest
utilise des modules CommonJS. Par exemple,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// etc...
};
});
S'il y a un dossier __mocks__
à côté d'un fichier que vous mockez, et que la factory n'est pas fournie, Vitest essaiera de trouver un fichier du même nom dans le sous-dossier __mocks__
et l'utilisera comme module réel. Si vous mockez une dépendance, Vitest essaiera de trouver un dossier __mocks__
à la racine du projet (par défaut, process.cwd()
). Vous pouvez indiquer à Vitest où se trouvent les dépendances via l'option de configuration deps.moduleDirectories
.
Par exemple, vous avez cette structure de fichiers :
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Si vous appelez vi.mock
dans un fichier de test sans factory ni options fournies, il trouvera un fichier dans le dossier __mocks__
à utiliser comme module :
import { vi } from 'vitest';
// axios est une exportation par défaut de `__mocks__/axios.js`
import axios from 'axios';
// increment est une exportation nommée de `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Notez que si vous n'appelez pas vi.mock
, les modules ne sont pas mockés automatiquement. Pour reproduire le comportement de mocking automatique de Jest, vous pouvez appeler vi.mock
pour chaque module requis dans setupFiles
.
S'il n'y a pas de dossier __mocks__
ou de factory fournie, Vitest importera le module original et mockera automatiquement toutes ses exportations. Pour les règles appliquées, voir algorithme.
vi.doMock
- Type :
(path: string, factory?: MockOptions | ((importOriginal: () => unknown) => unknown)) => void
- Type :
<T>(path: Promise<T>, factory?: MockOptions | ((importOriginal: () => T) => T | Promise<T>)) => void
Identique à vi.mock
, mais il n'est pas hoisté en haut du fichier, ce qui vous permet de référencer des variables dans la portée globale du fichier. La prochaine importation dynamique du module sera mockée.
WARNING
Cela ne mockera pas les modules qui ont été importés avant cet appel. N'oubliez pas que toutes les importations statiques dans ESM sont toujours hoistées, donc placer ceci avant une importation statique ne la forcera pas à être appelée avant l'importation :
vi.doMock('./increment.js'); // ceci sera appelé _après_ l'instruction d'importation
import { increment } from './increment.js';
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// le module n'est pas mocké, car vi.doMock n'est pas encore appelé
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// vous pouvez accéder aux variables à l'intérieur d'une factory
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('l\'importation du module suivant importe le module mocké', async () => {
// l'importation originale n'a pas été mockée, car vi.doMock est évalué après les importations
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// la nouvelle importation dynamique renvoie le module mocké
expect(mockedIncrement(1)).toBe(101);
expect(mockedIncrement(1)).toBe(102);
expect(mockedIncrement(1)).toBe(103);
});
vi.mocked
- Type :
<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
- Type :
<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
Utilitaire de typage pour TypeScript. Renvoie l'objet qui a été passé.
Lorsque partial
est true
, il attendra un Partial<T>
comme valeur de retour. Par défaut, cela indique seulement à TypeScript que les valeurs de premier niveau sont mockées. Vous pouvez passer { deep: true }
comme deuxième argument pour indiquer à TypeScript que l'objet entier est mocké, si c'est le cas.
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 est égal à 10', async () => {
vi.mocked(example.add).mockReturnValue(10);
expect(example.add(1, 1)).toBe(10);
});
test('valeur de retour mockée avec un typage seulement partiellement correct', 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 }) // ceci est une erreur de type
});
vi.importActual
- Type :
<T>(path: string) => Promise<T>
Importe un module, en contournant toutes les vérifications pour savoir s'il doit être mocké. Peut être utile si vous souhaitez mocker un module partiellement.
vi.mock('./example.js', async () => {
const originalModule = await vi.importActual('./example.js');
return { ...originalModule, get: vi.fn() };
});
vi.importMock
- Type :
<T>(path: string) => Promise<MaybeMockedDeep<T>>
Importe un module avec toutes ses propriétés (y compris les propriétés imbriquées) mockées. Suit les mêmes règles que vi.mock
. Pour les règles appliquées, voir algorithme.
vi.unmock
- Type :
(path: string | Promise<Module>) => void
Supprime le module du registre des mocks. Tous les appels à l'importation renverront le module original même s'il avait été mocké auparavant. Cet appel est hoisté en haut du fichier, il ne démockera donc que les modules définis dans setupFiles
, par exemple.
vi.doUnmock
- Type :
(path: string | Promise<Module>) => void
Identique à vi.unmock
, mais il n'est pas hoisté en haut du fichier. La prochaine importation du module importera le module original au lieu du mock. Cela n'annulera pas la simulation des modules précédemment importés.
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment est déjà mocké, car vi.mock est hoisté
increment(1) === 100;
// ceci est hoisté, et la factory est appelée avant l'importation à la ligne 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// tous les appels sont mockés, et `increment` renvoie toujours 100
increment(1) === 100;
increment(30) === 100;
// ceci n'est pas hoisté, donc une autre importation renverra un module non mocké
vi.doUnmock('./increment.js');
// ceci renvoie TOUJOURS 100, car `vi.doUnmock` ne réévalue pas un module
increment(1) === 100;
increment(30) === 100;
// la prochaine importation n'est pas mockée, maintenant `increment` est la fonction originale qui renvoie count + 1
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- Type :
() => Vitest
Réinitialise le registre des modules en effaçant le cache de tous les modules. Cela permet aux modules d'être réévalués lors d'une nouvelle importation. Les importations de niveau supérieur ne peuvent pas être réévaluées. Peut être utile pour isoler les modules dont l'état local entre en conflit entre les tests.
import { vi } from 'vitest';
import { data } from './data.js'; // Ne sera pas réévalué avant chaque test
beforeEach(() => {
vi.resetModules();
});
test('changer l\'état', async () => {
const mod = await import('./some/path.js'); // Sera réévalué
mod.changeLocalState('nouvelle valeur');
expect(mod.getLocalState()).toBe('nouvelle valeur');
});
test('le module a l\'ancien état', async () => {
const mod = await import('./some/path.js'); // Sera réévalué
expect(mod.getLocalState()).toBe('ancienne valeur');
});
WARNING
Ne réinitialise pas le registre des mocks. Pour effacer le registre des mocks, utilisez vi.unmock
ou vi.doUnmock
.
vi.dynamicImportSettled
Attend que toutes les importations dynamiques soient résolues. Utile si vous avez un appel synchrone qui déclenche l'importation d'un module que vous ne pouvez pas attendre par d'autres moyens.
import { expect, test } from 'vitest';
// impossible de suivre l'importation car la Promesse n'est pas retournée
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('les opérations sont résolues', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
Si, lors d'une importation dynamique, une autre importation dynamique est déclenchée, cette méthode attendra que toutes soient résolues.
Cette méthode attendra également le prochain tick de setTimeout
après la résolution de l'importation, afin que toutes les opérations synchrones soient terminées.
Mocking de fonctions et d'objets
Cette section décrit comment travailler avec les mocks de méthodes et remplacer les variables d'environnement et globales.
vi.fn
- Type :
(fn?: Function) => Mock
Crée un espion sur une fonction, bien qu'il puisse être créé sans en fournir une. Chaque fois qu'une fonction est invoquée, elle stocke ses arguments d'appel, ses valeurs de retour et ses instances. De plus, vous pouvez manipuler son comportement avec des méthodes. Si aucune fonction n'est donnée, le mock renverra undefined
lorsqu'il sera invoqué.
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+
- Type :
<T>(value: T) => MaybeMockedDeep<T>
Mocke en profondeur les propriétés et les méthodes d'un objet donné de la même manière que vi.mock()
mocke les exportations de modules. Voir automocking pour plus de détails.
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
- Type :
(fn: Function) => boolean
Vérifie qu'un paramètre donné est une fonction mockée. Si vous utilisez TypeScript, cela affinera également son type.
vi.clearAllMocks
Appelle .mockClear()
sur tous les espions. Cela effacera l'historique des mocks sans affecter leurs implémentations.
vi.resetAllMocks
Appelle .mockReset()
sur tous les espions. Cela effacera l'historique des mocks et réinitialisera l'implémentation de chaque mock à son état original.
vi.restoreAllMocks
Appelle .mockRestore()
sur tous les espions. Cela effacera l'historique des mocks, restaurera toutes les implémentations de mock originales et restaurera les descripteurs originaux des objets espionnés.
vi.spyOn
- Type :
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Crée un espion sur une méthode ou un getter/setter d'un objet à l'instar de vi.fn()
. Il renvoie une fonction mockée.
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
Dans les environnements qui prennent en charge la gestion explicite des ressources, vous pouvez utiliser using
au lieu de const
pour appeler automatiquement mockRestore
sur toute fonction mockée lorsque le bloc englobant est quitté. C'est particulièrement utile pour les méthodes espionnées :
it('appelle console.log', () => {
using spy = vi.spyOn(console, 'log').mockImplementation(() => {})
debug('message')
expect(spy).toHaveBeenCalled()
})
// console.log est restauré ici
TIP
Vous pouvez appeler vi.restoreAllMocks
dans afterEach
(ou activer test.restoreMocks
) pour restaurer toutes les méthodes à leurs implémentations originales. Cela restaurera le descripteur d'objet original, empêchant ainsi de modifier l'implémentation de la méthode :
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()); // toujours 42 !
TIP
Il n'est pas possible d'espionner les méthodes exportées dans le mode navigateur. Au lieu de cela, vous pouvez espionner chaque méthode exportée en appelant vi.mock("./file-path.js", { spy: true })
. Cela mockera chaque exportation mais maintiendra son implémentation intacte, vous permettant d'affirmer si la méthode a été appelée correctement.
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);
Et bien qu'il soit possible d'espionner les exportations dans jsdom
ou d'autres environnements Node.js, cela pourrait changer à l'avenir.
vi.stubEnv
- Type :
<T extends string>(name: T, value: T extends "PROD" | "DEV" | "SSR" ? boolean : string | undefined) => Vitest
Modifie la valeur de la variable d'environnement dans process.env
et import.meta.env
. Vous pouvez restaurer sa valeur via l'appel à vi.unstubAllEnvs
.
import { vi } from 'vitest';
// `process.env.NODE_ENV` et `import.meta.env.NODE_ENV`
// sont "development" avant d'appeler "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;
// ne change pas les autres envs
import.meta.env.MODE === 'development';
TIP
Vous pouvez également modifier la valeur en l'affectant directement, mais vous ne pourrez pas utiliser vi.unstubAllEnvs
pour restaurer la valeur précédente :
import.meta.env.MODE = 'test';
vi.unstubAllEnvs
- Type :
() => Vitest
Restaure toutes les valeurs import.meta.env
et process.env
qui ont été modifiées avec vi.stubEnv
. Lorsqu'elle est appelée pour la première fois, Vitest mémorise la valeur originale et la conservera jusqu'à ce que unstubAllEnvs
soit appelée à nouveau.
import { vi } from 'vitest';
// `process.env.NODE_ENV` et `import.meta.env.NODE_ENV`
// sont "development" avant d'appeler 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();
// restaure la valeur stockée avant le premier appel à "stubEnv"
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Type :
(name: string | number | symbol, value: unknown) => Vitest
Permet de modifier la valeur d'une variable globale. Vous pouvez restaurer sa valeur originale via l'appel à vi.unstubAllGlobals
.
import { vi } from 'vitest';
// `innerWidth` est "0" avant d'appeler stubGlobal
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// si vous utilisez jsdom ou happy-dom
window.innerWidth === 100;
TIP
Vous pouvez également modifier la valeur en l'affectant directement à globalThis
ou window
(si vous utilisez l'environnement jsdom
ou happy-dom
), mais vous ne pourrez pas utiliser vi.unstubAllGlobals
pour restaurer la valeur originale :
globalThis.innerWidth = 100;
// si vous utilisez jsdom ou happy-dom
window.innerWidth = 100;
vi.unstubAllGlobals
- Type :
() => Vitest
Restaure toutes les valeurs globales sur globalThis
/global
(et window
/top
/self
/parent
, si vous utilisez l'environnement jsdom
ou happy-dom
) qui ont été modifiées avec vi.stubGlobal
. Lorsqu'elle est appelée pour la première fois, Vitest mémorise la valeur originale et la conservera jusqu'à ce que unstubAllGlobals
soit appelée à nouveau.
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver est "undefined" avant d'appeler "stubGlobal"
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// si vous utilisez jsdom ou happy-dom
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// lève une ReferenceError, car elle n'est plus définie
IntersectionObserver === undefined;
Fake Timers
Cette section décrit comment travailler avec les fake timers.
vi.advanceTimersByTime
- Type :
(ms: number) => Vitest
Cette méthode invoquera chaque minuteur initié jusqu'à ce que le nombre de millisecondes spécifié soit écoulé ou que la file d'attente soit vide, selon la première de ces conditions.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- Type :
(ms: number) => Promise<Vitest>
Cette méthode invoquera chaque minuteur initié jusqu'à ce que le nombre de millisecondes spécifié soit écoulé ou que la file d'attente soit vide, selon la première de ces conditions. Cela inclura les minuteurs définis de manière asynchrone.
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersByTimeAsync(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- Type :
() => Vitest
Exécutera le prochain minuteur disponible. Utile pour effectuer des assertions entre chaque appel de minuteur. Vous pouvez l'appeler en chaînant les appels pour gérer les minuteurs vous-même.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Type :
() => Promise<Vitest>
Exécutera le prochain minuteur disponible et attendra qu'il soit résolu s'il a été défini de manière asynchrone. Utile pour effectuer des assertions entre chaque appel de minuteur.
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+
- Type :
() => Vitest
Similaire à vi.advanceTimersByTime
, mais avancera les minuteurs du nombre de millisecondes nécessaires pour exécuter les rappels actuellement planifiés avec requestAnimationFrame
.
let frameRendered = false;
requestAnimationFrame(() => {
frameRendered = true;
});
vi.advanceTimersToNextFrame();
expect(frameRendered).toBe(true);
vi.getTimerCount
- Type :
() => number
Renvoie le nombre de minuteurs en attente.
vi.clearAllTimers
Supprime tous les minuteurs qui sont planifiés pour s'exécuter. Ces minuteurs ne seront jamais exécutés.
vi.getMockedSystemTime
- Type :
() => Date | null
Renvoie la date actuelle mockée. Si la date n'est pas mockée, la méthode renverra null
.
vi.getRealSystemTime
- Type :
() => number
Lorsque vous utilisez vi.useFakeTimers
, les appels Date.now
sont mockés. Si vous avez besoin d'obtenir l'heure réelle en millisecondes, vous pouvez appeler cette fonction.
vi.runAllTicks
- Type :
() => Vitest
Appelle chaque microtâche qui a été mise en attente par process.nextTick
. Cela exécutera également toutes les microtâches qui se sont planifiées elles-mêmes.
vi.runAllTimers
- Type :
() => Vitest
Cette méthode invoquera chaque minuteur initié jusqu'à ce que la file d'attente des timers soit vide. Cela signifie que chaque minuteur appelé pendant runAllTimers
sera exécuté. Si vous avez un intervalle infini, une exception sera levée après 10 000 tentatives (peut être configuré avec 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
- Type :
() => Promise<Vitest>
Cette méthode invoquera de manière asynchrone chaque minuteur initié jusqu'à ce que la file d'attente des timers soit vide. Cela signifie que chaque minuteur appelé pendant runAllTimersAsync
sera exécuté, même les minuteurs asynchrones. Si vous avez un intervalle infini, une exception sera levée après 10 000 tentatives (peut être configuré avec fakeTimers.loopLimit
).
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- Type :
() => Vitest
Cette méthode exécutera chaque minuteur qui a été planifié après l'appel de vi.useFakeTimers
. Elle ne déclenchera aucun minuteur qui a été planifié durant son exécution.
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Type :
() => Promise<Vitest>
Cette méthode appellera de manière asynchrone chaque minuteur qui a été planifié après l'appel de vi.useFakeTimers
, même les asynchrones. Elle ne déclenchera aucun minuteur qui a été planifié durant son exécution.
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
- Type :
(date: string | number | Date) => void
Si les fake timers sont activés, cette méthode simule un utilisateur modifiant l'horloge système (ce qui affectera les API liées à la date comme hrtime
, performance.now
ou new Date()
). Cependant, elle ne déclenchera aucun minuteur. Si les fake timers ne sont pas activés, cette méthode ne mockera que les appels Date.*
.
Utile si vous avez besoin de tester quelque chose qui dépend de la date actuelle, par exemple les appels Luxon dans votre code.
Accepte les mêmes arguments de type chaîne et nombre que le constructeur Date
.
const date = new Date(1998, 11, 19);
vi.useFakeTimers();
vi.setSystemTime(date);
expect(Date.now()).toBe(date.valueOf());
vi.useRealTimers();
vi.useFakeTimers
- Type :
(config?: FakeTimerInstallOpts) => Vitest
Pour activer le mocking des timers, vous devez appeler cette méthode. Elle interceptera tous les appels ultérieurs aux minuteurs (tels que setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
et Date
) jusqu'à ce que vi.useRealTimers()
soit appelé.
Le mocking de nextTick
n'est pas pris en charge lors de l'exécution de Vitest dans node:child_process
en utilisant --pool=forks
. NodeJS utilise process.nextTick
en interne dans node:child_process
et se bloque lorsqu'il est mocké. Le mocking de nextTick
est pris en charge lors de l'exécution de Vitest avec --pool=threads
.
L'implémentation est basée en interne sur @sinonjs/fake-timers
.
TIP
vi.useFakeTimers()
ne mocke pas automatiquement process.nextTick
et queueMicrotask
. Mais vous pouvez l'activer en spécifiant l'option toFake
: vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })
.
vi.isFakeTimers
- Type :
() => boolean
Renvoie true
si les fake timers sont activés.
vi.useRealTimers
- Type :
() => Vitest
Lorsque les timers ont terminé leur exécution, vous pouvez appeler cette méthode pour restaurer les mocks de timers à leurs implémentations originales. Tous les minuteurs qui étaient programmés auparavant seront annulés.
Divers
Un ensemble de fonctions utilitaires fournies par Vitest.
vi.waitFor
- Type :
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Attend que le rappel soit exécuté avec succès. Si le rappel lève une erreur ou renvoie une promesse rejetée, l'attente se poursuivra jusqu'à ce qu'il réussisse ou expire.
Si les options sont définies sur un nombre, l'effet est équivalent à { timeout: options }
.
C'est très utile lorsque vous devez attendre qu'une action asynchrone se termine, par exemple, lorsque vous démarrez un serveur et que vous devez attendre qu'il démarre.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Le serveur a démarré avec succès', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) {
throw new Error('Le serveur n\'a pas démarré');
}
console.log('Serveur démarré');
},
{
timeout: 500, // la valeur par défaut est 1000
interval: 20, // la valeur par défaut est 50
}
);
expect(server.isReady).toBe(true);
});
Cela fonctionne également pour les rappels asynchrones
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('L\'élément existe dans un DOM', async () => {
// commencer à peupler le DOM
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// essayer d'obtenir l'élément jusqu'à ce qu'il existe
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // la valeur par défaut est 1000
interval: 20, // la valeur par défaut est 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Si vi.useFakeTimers
est utilisé, vi.waitFor
appelle automatiquement vi.advanceTimersByTime(interval)
à chaque vérification.
vi.waitUntil
- Type :
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Ceci est similaire à vi.waitFor
, mais si le rappel lève des erreurs, l'exécution est immédiatement interrompue et une erreur est renvoyée. Si le rappel renvoie une valeur falsy, la prochaine vérification continuera jusqu'à ce qu'une valeur truthy soit renvoyée. C'est utile lorsque vous devez attendre que quelque chose existe avant de passer à l'étape suivante.
Regardez l'exemple ci-dessous. Nous pouvons utiliser vi.waitUntil
pour attendre que l'élément apparaisse sur la page, puis nous pouvons faire quelque chose avec l'élément.
import { expect, test, vi } from 'vitest';
test('Le rendu de l\'élément est correct', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // la valeur par défaut est 1000
interval: 20, // la valeur par défaut est 50
});
// faire quelque chose avec l'élément
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted
- Type :
<T>(factory: () => T) => T
Toutes les instructions import
statiques dans les modules ES sont hoistées en haut du fichier, de sorte que tout code défini avant les importations sera exécuté après l'évaluation des importations.
Cependant, il peut être utile d'invoquer certains effets secondaires comme le mocking de dates avant d'importer un module.
Pour contourner cette limitation, vous pouvez réécrire les importations statiques en importations dynamiques comme ceci :
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
Lors de l'exécution de vitest
, vous pouvez le faire automatiquement avec la méthode vi.hoisted
. En interne, Vitest convertira les importations statiques en importations dynamiques avec des live-bindings préservés.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
LES IMPORTATIONS NE SONT PAS DISPONIBLES
Exécuter du code avant les importations signifie que vous ne pouvez pas accéder aux variables importées car elles ne sont pas encore définies :
import { value } from './some/module.js';
vi.hoisted(() => { value }); // lève une erreur
Ce code produira une erreur :
Impossible d'accéder à '__vi_import_0__' avant l'initialisation
Si vous avez besoin d'accéder à une variable d'un autre module à l'intérieur de vi.hoisted
, utilisez l'importation dynamique :
await vi.hoisted(async () => {
const { value } = await import('./some/module.js');
});
Cependant, il est déconseillé d'importer quoi que ce soit à l'intérieur de vi.hoisted
car les importations sont déjà hoistées - si vous avez besoin d'exécuter quelque chose avant l'exécution des tests, faites-le simplement dans le module importé lui-même.
Cette méthode renvoie la valeur qui a été renvoyée par la factory. Vous pouvez utiliser cette valeur dans vos factories vi.mock
si vous avez besoin d'un accès facile aux variables définies localement :
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);
Notez que cette méthode peut également être appelée de manière asynchrone même si votre environnement ne prend pas en charge l'await de niveau supérieur :
const json = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- Type :
RuntimeConfig
Met à jour la configuration du fichier de test actuel. Cette méthode ne prend en charge que les options de configuration qui s'appliqueront au fichier de test actuel :
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// prend en charge l'objet entier
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// ne prend en charge que "sequence.hooks"
},
});
vi.resetConfig
- Type :
RuntimeConfig
Si vi.setConfig
a été appelé précédemment, cela réinitialisera la configuration à son état original.