Simulation
Lors de la rédaction de tests, il est souvent nécessaire de créer une version "fictive" d'un service, qu'il soit interne ou externe. C'est ce que l'on appelle communément le mocking (ou simulation). Vitest met à votre disposition des fonctions utilitaires via son module vi
. Vous pouvez l'importer depuis vitest
ou y accéder globalement si la configuration globals
est activée.
WARNING
N'oubliez jamais de réinitialiser ou de restaurer les mocks avant ou après chaque test afin d'annuler les modifications d'état des mocks entre les exécutions. Pour plus d'informations, consultez la documentation de mockReset
.
Si vous n'êtes pas familier avec les méthodes vi.fn
, vi.mock
ou vi.spyOn
, veuillez consulter la section API au préalable.
Dates
Il est parfois nécessaire de contrôler la date pour garantir la cohérence des tests. Vitest utilise le package @sinonjs/fake-timers
pour manipuler les minuteurs et la date système. Vous trouverez plus de détails sur l'API spécifique ici.
Exemple
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
const businessHours = [9, 17];
function purchase() {
const currentHour = new Date().getHours();
const [open, close] = businessHours;
if (currentHour > open && currentHour < close) {
return { message: 'Success' };
}
return { message: 'Error' };
}
describe('purchasing flow', () => {
beforeEach(() => {
// Indiquer à Vitest que nous utilisons le temps simulé
vi.useFakeTimers();
});
afterEach(() => {
// Restaurer la date après chaque exécution de test
vi.useRealTimers();
});
it('allows purchases within business hours', () => {
// Définir l'heure pendant les heures ouvrables
const date = new Date(2000, 1, 1, 13);
vi.setSystemTime(date);
// L'accès à Date.now() renverra la date définie ci-dessus
expect(purchase()).toEqual({ message: 'Success' });
});
it('disallows purchases outside of business hours', () => {
// Définir l'heure en dehors des heures ouvrables
const date = new Date(2000, 1, 1, 19);
vi.setSystemTime(date);
// L'accès à Date.now() renverra la date définie ci-dessus
expect(purchase()).toEqual({ message: 'Error' });
});
});
Fonctions
La simulation de fonctions peut être divisée en deux catégories : l'espionnage et la simulation.
Parfois, il suffit de valider si une fonction spécifique a été appelée (et éventuellement avec quels arguments). Dans ces cas, un espion est suffisant, que vous pouvez utiliser directement avec vi.spyOn()
([pour plus d'informations, consultez ici]).
Cependant, les espions ne peuvent que vous aider à espionner les fonctions ; ils ne sont pas capables de modifier leur implémentation. Dans le cas où nous devons créer une version fictive (ou simulée) d'une fonction, nous pouvons utiliser vi.fn()
([pour plus d'informations, consultez ici]).
Nous utilisons Tinyspy comme base pour la simulation de fonctions, mais nous avons notre propre wrapper pour assurer la compatibilité avec jest
. Les méthodes vi.fn()
et vi.spyOn()
partagent les mêmes méthodes, cependant seul le résultat de vi.fn()
peut être appelé.
Exemple
import { afterEach, describe, expect, it, vi } from 'vitest';
const messages = {
items: [
{ message: 'Simple test message', from: 'Testman' },
// ...
],
getLatest, // peut également être un accesseur (getter) ou un mutateur (setter) si pris en charge
};
function getLatest(index = messages.items.length - 1) {
return messages.items[index];
}
describe('reading messages', () => {
afterEach(() => {
vi.restoreAllMocks();
});
it('should get the latest message with a spy', () => {
const spy = vi.spyOn(messages, 'getLatest');
expect(spy.getMockName()).toEqual('getLatest');
expect(messages.getLatest()).toEqual(
messages.items[messages.items.length - 1]
);
expect(spy).toHaveBeenCalledTimes(1);
spy.mockImplementationOnce(() => 'access-restricted');
expect(messages.getLatest()).toEqual('access-restricted');
expect(spy).toHaveBeenCalledTimes(2);
});
it('should get with a mock', () => {
const mock = vi.fn().mockImplementation(getLatest);
expect(mock()).toEqual(messages.items[messages.items.length - 1]);
expect(mock).toHaveBeenCalledTimes(1);
mock.mockImplementationOnce(() => 'access-restricted');
expect(mock()).toEqual('access-restricted');
expect(mock).toHaveBeenCalledTimes(2);
expect(mock()).toEqual(messages.items[messages.items.length - 1]);
expect(mock).toHaveBeenCalledTimes(3);
});
});
Plus
Variables globales
Vous pouvez simuler des variables globales qui ne sont pas présentes avec jsdom
ou node
en utilisant l'utilitaire vi.stubGlobal
. Cela placera la valeur de la variable globale dans l'objet globalThis
.
import { vi } from 'vitest';
const IntersectionObserverMock = vi.fn(() => ({
disconnect: vi.fn(),
observe: vi.fn(),
takeRecords: vi.fn(),
unobserve: vi.fn(),
}));
vi.stubGlobal('IntersectionObserver', IntersectionObserverMock);
// vous pouvez maintenant y accéder en tant que `IntersectionObserver` ou `window.IntersectionObserver`
Modules
La simulation de modules permet d'intercepter les bibliothèques tierces invoquées dans d'autres parties du code, vous permettant ainsi de tester les arguments, la sortie ou même de redéfinir leur implémentation.
Consultez la section API de vi.mock()
pour une description plus détaillée de l'API.
Algorithme d'auto-simulation
Si votre code importe un module simulé, sans aucun fichier __mocks__
associé ou factory
pour ce module, Vitest simulera le module lui-même en l'invoquant et en simulant chaque exportation.
Les principes suivants s'appliquent :
- Tous les tableaux seront rendus vides.
- Toutes les primitives et collections resteront inchangées.
- Tous les objets seront clonés en profondeur.
- Toutes les instances de classes et leurs prototypes seront clonés en profondeur.
Modules virtuels
Vitest prend en charge la simulation des modules virtuels de Vite. Cela fonctionne différemment de la façon dont les modules virtuels sont traités dans Jest. Au lieu de passer virtual: true
à une fonction vi.mock
, vous devez indiquer à Vite que le module existe, sinon il échouera lors de l'analyse. Vous pouvez le faire de plusieurs manières :
- Fournir un alias
import { defineConfig } from 'vitest/config';
import { resolve } from 'node:path';
export default defineConfig({
test: {
alias: {
'$app/forms': resolve('./mocks/forms.js'),
},
},
});
- Fournir un plugin qui résout un module virtuel
import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [
{
name: 'virtual-modules',
resolveId(id) {
if (id === '$app/forms') {
return 'virtual:$app/forms';
}
},
},
],
});
L'avantage de la deuxième approche est que vous pouvez créer dynamiquement différents points d'entrée virtuels. Si vous redirigez plusieurs modules virtuels vers un seul fichier, tous seront impactés par vi.mock
, alors assurez-vous d'utiliser des identifiants uniques.
Pièges de la simulation
Sachez qu'il n'est pas possible de simuler les appels aux méthodes qui sont invoquées par d'autres méthodes du même fichier. Par exemple, dans ce code :
export function foo() {
return 'foo';
}
export function foobar() {
return `${foo()}bar`;
}
Il n'est pas possible de simuler la méthode foo
de l'extérieur car elle est référencée directement. Ainsi, ce code n'aura aucun effet sur l'appel de foo
à l'intérieur de foobar
(mais il affectera l'appel de foo
dans d'autres modules) :
import { vi } from 'vitest';
import * as mod from './foobar.js';
// cela n'affectera "foo" qu'en dehors du module original
vi.spyOn(mod, 'foo');
vi.mock('./foobar.js', async importOriginal => {
return {
...(await importOriginal<typeof import('./foobar.js')>()),
// cela n'affectera "foo" qu'en dehors du module original
foo: () => 'mocked',
};
});
Vous pouvez confirmer ce comportement en fournissant directement l'implémentation à la méthode foobar
:
import * as mod from './foobar.js';
vi.spyOn(mod, 'foo');
// foo exporté référence la méthode simulée
mod.foobar(mod.foo);
export function foo() {
return 'foo';
}
export function foobar(injectedFoo) {
return injectedFoo === foo; // false
}
C'est le comportement attendu. Cela indique généralement une mauvaise pratique de codage lorsque la simulation est utilisée de cette manière. Envisagez de refactoriser votre code en plusieurs fichiers ou d'améliorer l'architecture de votre application en utilisant des techniques telles que l'injection de dépendances.
Exemple
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Client } from 'pg';
import { failure, success } from './handlers.js';
// obtenir les tâches
export async function getTodos(event, context) {
const client = new Client({
// ...clientOptions
});
await client.connect();
try {
const result = await client.query('SELECT * FROM todos;');
client.end();
return success({
message: `${result.rowCount} item(s) returned`,
data: result.rows,
status: true,
});
} catch (e) {
console.error(e.stack);
client.end();
return failure({ message: e, status: false });
}
}
vi.mock('pg', () => {
const Client = vi.fn();
Client.prototype.connect = vi.fn();
Client.prototype.query = vi.fn();
Client.prototype.end = vi.fn();
return { Client };
});
vi.mock('./handlers.js', () => {
return {
success: vi.fn(),
failure: vi.fn(),
};
});
describe('get a list of todo items', () => {
let client;
beforeEach(() => {
client = new Client();
});
afterEach(() => {
vi.clearAllMocks();
});
it('should return items successfully', async () => {
client.query.mockResolvedValueOnce({ rows: [], rowCount: 0 });
await getTodos();
expect(client.connect).toBeCalledTimes(1);
expect(client.query).toBeCalledWith('SELECT * FROM todos;');
expect(client.end).toBeCalledTimes(1);
expect(success).toBeCalledWith({
message: '0 item(s) returned',
data: [],
status: true,
});
});
it('should throw an error', async () => {
const mError = new Error('Unable to retrieve rows');
client.query.mockRejectedValueOnce(mError);
await getTodos();
expect(client.connect).toBeCalledTimes(1);
expect(client.query).toBeCalledWith('SELECT * FROM todos;');
expect(client.end).toBeCalledTimes(1);
expect(failure).toBeCalledWith({ message: mError, status: false });
});
});
Système de fichiers
La simulation du système de fichiers garantit que les tests ne dépendent pas du système de fichiers réel, les rendant ainsi plus fiables et prévisibles. Cette isolation permet d'éviter les effets secondaires des tests précédents. Elle permet de tester des conditions d'erreur et des cas limites qui pourraient être difficiles ou impossibles à reproduire avec un système de fichiers réel, tels que des problèmes de permissions, des scénarios de disque saturé ou des erreurs de lecture/écriture.
Vitest ne fournit aucune API de simulation de système de fichiers nativement. Vous pouvez utiliser vi.mock
pour simuler le module fs
manuellement, mais cela est difficile à maintenir. Au lieu de cela, nous vous recommandons d'utiliser memfs
pour le faire à votre place. memfs
crée un système de fichiers en mémoire, qui simule les opérations du système de fichiers sans toucher le disque réel. Cette approche est rapide et sûre, évitant ainsi tout effet secondaire potentiel sur le système de fichiers réel.
Exemple
Pour rediriger automatiquement chaque appel fs
vers memfs
, vous pouvez créer les fichiers __mocks__/fs.cjs
et __mocks__/fs/promises.cjs
à la racine de votre projet :
// nous pouvons aussi utiliser `import`, mais alors
// chaque exportation devrait être explicitement définie
const { fs } = require('memfs');
module.exports = fs;
// nous pouvons aussi utiliser `import`, mais alors
// chaque exportation devrait être explicitement définie
const { fs } = require('memfs');
module.exports = fs.promises;
import { readFileSync } from 'node:fs';
export function readHelloWorld(path) {
return readFileSync(path, 'utf-8');
}
import { beforeEach, expect, it, vi } from 'vitest';
import { fs, vol } from 'memfs';
import { readHelloWorld } from './read-hello-world.js';
// indiquer à Vitest d'utiliser la simulation du module fs du dossier __mocks__
// cela peut être fait dans un fichier de configuration si fs doit toujours être simulé
vi.mock('node:fs');
vi.mock('node:fs/promises');
beforeEach(() => {
// réinitialiser l'état du système de fichiers en mémoire
vol.reset();
});
it('should return correct text', () => {
const path = '/hello-world.txt';
fs.writeFileSync(path, 'hello world');
const text = readHelloWorld(path);
expect(text).toBe('hello world');
});
it('can return a value multiple times', () => {
// vous pouvez utiliser vol.fromJSON pour définir plusieurs fichiers
vol.fromJSON(
{
'./dir1/hw.txt': 'hello dir1',
'./dir2/hw.txt': 'hello dir2',
},
// répertoire de travail par défaut
'/tmp'
);
expect(readHelloWorld('/tmp/dir1/hw.txt')).toBe('hello dir1');
expect(readHelloWorld('/tmp/dir2/hw.txt')).toBe('hello dir2');
});
Requêtes
Étant donné que Vitest s'exécute dans Node.js, la simulation des requêtes réseau est délicate ; les API web ne sont pas disponibles, nous avons donc besoin d'un outil qui simulera le comportement réseau. Nous recommandons Mock Service Worker pour y parvenir. Il vous permet de simuler les requêtes réseau http
, WebSocket
et GraphQL
, et est indépendant du framework.
Mock Service Worker (MSW) fonctionne en interceptant les requêtes que vos tests effectuent, ce qui permet de l'utiliser sans modifier votre code d'application. Dans le navigateur, cela utilise l'API Service Worker. Dans Node.js (et pour Vitest), il utilise la bibliothèque @mswjs/interceptors
. Pour en savoir plus sur MSW, lisez leur introduction
Configuration
Vous pouvez l'utiliser de la manière suivante dans votre fichier de configuration :
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { http, HttpResponse } from 'msw';
const posts = [
{
userId: 1,
id: 1,
title: 'first post title',
body: 'first post body',
},
// ...
];
export const restHandlers = [
http.get('https://rest-endpoint.example/path/to/posts', () => {
return HttpResponse.json(posts);
}),
];
const server = setupServer(...restHandlers);
// Démarrer le serveur avant tous les tests
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
// Fermer le serveur après tous les tests
afterAll(() => server.close());
// Réinitialiser les gestionnaires après chaque test pour assurer l'isolation des tests
afterEach(() => server.resetHandlers());
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { graphql, HttpResponse } from 'msw';
const posts = [
{
userId: 1,
id: 1,
title: 'first post title',
body: 'first post body',
},
// ...
];
const graphqlHandlers = [
graphql.query('ListPosts', () => {
return HttpResponse.json({
data: { posts },
});
}),
];
const server = setupServer(...graphqlHandlers);
// Démarrer le serveur avant tous les tests
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
// Fermer le serveur après tous les tests
afterAll(() => server.close());
// Réinitialiser les gestionnaires après chaque test pour assurer l'isolation des tests
afterEach(() => server.resetHandlers());
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { ws } from 'msw';
const chat = ws.link('wss://chat.example.com');
const wsHandlers = [
chat.addEventListener('connection', ({ client }) => {
client.addEventListener('message', event => {
console.log('Received message from client:', event.data);
// Renvoyer le message reçu au client
client.send(`Server received: ${event.data}`);
});
}),
];
const server = setupServer(...wsHandlers);
// Démarrer le serveur avant tous les tests
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
// Fermer le serveur après tous les tests
afterAll(() => server.close());
// Réinitialiser les gestionnaires après chaque test pour assurer l'isolation des tests
afterEach(() => server.resetHandlers());
La configuration du serveur avec
onUnhandledRequest: 'error'
garantit qu'une erreur est levée chaque fois qu'une requête n'a pas de gestionnaire correspondant.
Plus
MSW offre de nombreuses autres possibilités. Vous pouvez accéder aux cookies et aux paramètres de requête, définir des réponses d'erreur simulées, et bien plus encore ! Pour voir tout ce que vous pouvez faire avec MSW, lisez leur documentation.
Minuteurs
Lorsque nous testons du code qui implique des délais d'attente ou des intervalles, au lieu de laisser nos tests attendre ou dépasser le délai, nous pouvons accélérer nos tests en utilisant des minuteurs "fictifs" qui simulent les appels à setTimeout
et setInterval
.
Consultez la section API de vi.useFakeTimers
pour une description plus détaillée de l'API.
Exemple
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
function executeAfterTwoHours(func) {
setTimeout(func, 1000 * 60 * 60 * 2); // 2 heures
}
function executeEveryMinute(func) {
setInterval(func, 1000 * 60); // 1 minute
}
const mock = vi.fn(() => console.log('executed'));
describe('delayed execution', () => {
beforeEach(() => {
vi.useFakeTimers();
});
afterEach(() => {
vi.restoreAllMocks();
});
it('should execute the function', () => {
executeAfterTwoHours(mock);
vi.runAllTimers();
expect(mock).toHaveBeenCalledTimes(1);
});
it('should not execute the function', () => {
executeAfterTwoHours(mock);
// avancer de 2ms ne déclenchera pas l'exécution de la fonction
vi.advanceTimersByTime(2);
expect(mock).not.toHaveBeenCalled();
});
it('should execute every minute', () => {
executeEveryMinute(mock);
vi.advanceTimersToNextTimer();
expect(mock).toHaveBeenCalledTimes(1);
vi.advanceTimersToNextTimer();
expect(mock).toHaveBeenCalledTimes(2);
});
});
Classes
Vous pouvez simuler une classe entière avec un seul appel vi.fn
– puisque toutes les classes sont aussi des fonctions, cela fonctionne nativement. Sachez qu'actuellement Vitest ne prend pas en charge le mot-clé new
, donc new.target
est toujours undefined
dans le corps d'une fonction.
class Dog {
name: string;
constructor(name: string) {
this.name = name;
}
static getType(): string {
return 'animal';
}
greet = (): string => {
return `Hi! My name is ${this.name}!`;
};
speak(): string {
return 'bark!';
}
isHungry() {}
feed() {}
}
Nous pouvons recréer cette classe avec des fonctions ES5 :
const Dog = vi.fn(function (name) {
this.name = name;
// simuler les méthodes d'instance dans le constructeur, chaque instance aura son propre espion
this.greet = vi.fn(() => `Hi! My name is ${this.name}!`);
});
// notez que les méthodes statiques sont simulées directement sur la fonction,
// et non sur l'instance de la classe
Dog.getType = vi.fn(() => 'mocked animal');
// simuler les méthodes "speak" et "feed" sur chaque instance d'une classe
// toutes les instances `new Dog()` hériteront et partageront ces espions
Dog.prototype.speak = vi.fn(() => 'loud bark!');
Dog.prototype.feed = vi.fn();
WARNING
Si une valeur non primitive est renvoyée par la fonction constructeur, cette valeur deviendra le résultat de la nouvelle expression. Dans ce cas, le [[Prototype]]
peut ne pas être correctement associé :
const CorrectDogClass = vi.fn(function (name) {
this.name = name;
});
const IncorrectDogClass = vi.fn(name => ({
name,
}));
const Marti = new CorrectDogClass('Marti');
const Newt = new IncorrectDogClass('Newt');
Marti instanceof CorrectDogClass; // ✅ true
Newt instanceof IncorrectDogClass; // ❌ false!
QUAND L'UTILISER ?
Généralement, vous recréeriez une classe comme celle-ci à l'intérieur de la fabrique de modules si la classe est réexportée depuis un autre module :
import { Dog } from './dog.js';
vi.mock(import('./dog.js'), () => {
const Dog = vi.fn();
Dog.prototype.feed = vi.fn();
// ... autres mocks
return { Dog };
});
Cette méthode peut également être utilisée pour passer une instance d'une classe à une fonction qui implémente la même interface :
function feed(dog: Dog) {
// ...
}
import { expect, test, vi } from 'vitest';
import { feed } from '../src/feed.js';
const Dog = vi.fn();
Dog.prototype.feed = vi.fn();
test('can feed dogs', () => {
const dogMax = new Dog('Max');
feed(dogMax);
expect(dogMax.feed).toHaveBeenCalled();
expect(dogMax.isHungry()).toBe(false);
});
Maintenant, lorsque nous créons une nouvelle instance de la classe Dog
, sa méthode speak
(ainsi que feed
et greet
) est déjà simulée :
const Cooper = new Dog('Cooper');
Cooper.speak(); // loud bark!
Cooper.greet(); // Hi! My name is Cooper!
// vous pouvez utiliser les assertions intégrées pour vérifier la validité de l'appel
expect(Cooper.speak).toHaveBeenCalled();
expect(Cooper.greet).toHaveBeenCalled();
const Max = new Dog('Max');
// les méthodes assignées au prototype sont partagées entre les instances
expect(Max.speak).toHaveBeenCalled();
expect(Max.greet).not.toHaveBeenCalled();
Nous pouvons réaffecter la valeur de retour pour une instance spécifique :
const dog = new Dog('Cooper');
// "vi.mocked" est un utilitaire de typage, car
// TypeScript ne sait pas que Dog est une classe simulée,
// il enveloppe toute fonction dans un type MockInstance<T>
// sans valider si la fonction est une simulation
vi.mocked(dog.speak).mockReturnValue('woof woof');
dog.speak(); // woof woof
Pour simuler la propriété, nous pouvons utiliser la méthode vi.spyOn(dog, 'name', 'get')
. Cela permet d'utiliser des assertions d'espion sur la propriété simulée :
const dog = new Dog('Cooper');
const nameSpy = vi.spyOn(dog, 'name', 'get').mockReturnValue('Max');
expect(dog.name).toBe('Max');
expect(nameSpy).toHaveBeenCalledTimes(1);
TIP
Vous pouvez également espionner les accesseurs (getters) et les mutateurs (setters) en utilisant la même méthode.
Aide-mémoire
INFO
vi
dans les exemples ci-dessous est importé directement de vitest
. Vous pouvez également l'utiliser globalement, si vous activez l'option globals
en la définissant à true
dans votre configuration.
Je veux…
Simuler des variables exportées
export const getter = 'variable';
import * as exports from './example.js';
vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked');
Simuler une fonction exportée
- Exemple avec
vi.mock
:
WARNING
N'oubliez pas qu'un appel vi.mock
est remonté en haut du fichier (hoisted). Il sera toujours exécuté avant toutes les importations.
export function method() {}
import { method } from './example.js';
vi.mock('./example.js', () => ({
method: vi.fn(),
}));
- Exemple avec
vi.spyOn
:
import * as exports from './example.js';
vi.spyOn(exports, 'method').mockImplementation(() => {});
Simuler l'implémentation d'une classe exportée
- Exemple avec
vi.mock
et.prototype
:
export class SomeClass {}
import { SomeClass } from './example.js';
vi.mock(import('./example.js'), () => {
const SomeClass = vi.fn();
SomeClass.prototype.someMethod = vi.fn();
return { SomeClass };
});
// SomeClass.mock.instances aura SomeClass
- Exemple avec
vi.spyOn
:
import * as mod from './example.js';
const SomeClass = vi.fn();
SomeClass.prototype.someMethod = vi.fn();
vi.spyOn(mod, 'SomeClass').mockImplementation(SomeClass);
Espionner un objet retourné par une fonction
- Exemple utilisant le cache :
export function useObject() {
return { method: () => true };
}
import { useObject } from './example.js';
const obj = useObject();
obj.method();
import { useObject } from './example.js';
vi.mock(import('./example.js'), () => {
let _cache;
const useObject = () => {
if (!_cache) {
_cache = {
method: vi.fn(),
};
}
// maintenant, chaque fois que useObject() est appelé, il retournera la même référence d'objet
return _cache;
};
return { useObject };
});
const obj = useObject();
// obj.method a été appelé à l'intérieur de some-path
expect(obj.method).toHaveBeenCalled();
Simuler une partie d'un module
import { mocked, original } from './some-path.js';
vi.mock(import('./some-path.js'), async importOriginal => {
const mod = await importOriginal();
return {
...mod,
mocked: vi.fn(),
};
});
original(); // a le comportement original
mocked(); // est une fonction espionne
WARNING
N'oubliez pas que cela ne simule que l'accès externe. Dans cet exemple, si original
appelle mocked
en interne, il appellera toujours la fonction définie dans le module, et non dans la fabrique de mocks.
Simuler la date actuelle
Pour simuler l'heure de Date
, vous pouvez utiliser la fonction d'aide vi.setSystemTime
. Cette valeur ne sera pas automatiquement réinitialisée entre les tests.
Sachez que l'utilisation de vi.useFakeTimers
modifie également l'heure de l'objet Date
.
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// réinitialiser le temps simulé
vi.useRealTimers();
Simuler une variable globale
Vous pouvez définir une variable globale en assignant une valeur à globalThis
ou en utilisant l'utilitaire vi.stubGlobal
. Lorsque vous utilisez vi.stubGlobal
, elle ne sera pas automatiquement réinitialisée entre les tests, sauf si vous activez l'option de configuration unstubGlobals
ou appelez vi.unstubAllGlobals
.
vi.stubGlobal('__VERSION__', '1.0.0');
expect(__VERSION__).toBe('1.0.0');
Simuler import.meta.env
- Pour modifier une variable d'environnement, vous pouvez simplement lui assigner une nouvelle valeur.
WARNING
La valeur de la variable d'environnement ne sera pas automatiquement réinitialisée entre les tests.
import { beforeEach, expect, it } from 'vitest';
// avant d'exécuter les tests, "VITE_ENV" est à "test"
const originalViteEnv = import.meta.env.VITE_ENV;
beforeEach(() => {
import.meta.env.VITE_ENV = originalViteEnv;
});
it('changes value', () => {
import.meta.env.VITE_ENV = 'staging';
expect(import.meta.env.VITE_ENV).toBe('staging');
});
- Si vous souhaitez réinitialiser automatiquement la ou les valeurs, vous pouvez utiliser l'utilitaire
vi.stubEnv
avec l'option de configurationunstubEnvs
activée (ou appelervi.unstubAllEnvs
manuellement dans un hookbeforeEach
) :
import { expect, it, vi } from 'vitest';
// avant d'exécuter les tests, "VITE_ENV" est à "test"
import.meta.env.VITE_ENV === 'test';
it('changes value', () => {
vi.stubEnv('VITE_ENV', 'staging');
expect(import.meta.env.VITE_ENV).toBe('staging');
});
it('the value is restored before running an other test', () => {
expect(import.meta.env.VITE_ENV).toBe('test');
});
export default defineConfig({
test: {
unstubEnvs: true,
},
});