Mocking
Lors de l'écriture de tests, il est inévitable de devoir créer une version "simulée" d'un service interne ou externe. C'est ce que l'on appelle communément le mocking. Vitest met à disposition des fonctions utilitaires pour vous y aider via son utilitaire vi. Vous pouvez l'importer avec import { vi } from 'vitest'
ou y accéder globalement (lorsque la configuration globale est activée).
WARNING
N'oubliez jamais de réinitialiser ou de restaurer les mocks avant ou après chaque exécution de test afin d'annuler les changements d'état entre les exécutions ! Consultez la documentation de mockReset
pour plus d'informations.
Si vous souhaitez aborder le sujet directement, consultez la section API. Sinon, continuez à lire pour explorer plus en profondeur le monde du mocking.
Dates
Parfois, il est nécessaire de contrôler la date pour assurer la cohérence des tests. Vitest utilise le package @sinonjs/fake-timers
pour manipuler les temporisateurs, ainsi que la date système. Vous pouvez en savoir plus sur l'API spécifique en détail 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 un temps simulé
vi.useFakeTimers();
});
afterEach(() => {
// Rétablir 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);
// 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);
// Date.now() renverra la date définie ci-dessus
expect(purchase()).toEqual({ message: 'Error' });
});
});
Fonctions
Le mocking de fonctions peut être divisé en deux catégories distinctes : l'espionnage et le mocking.
Parfois, il suffit de vérifier si une fonction spécifique a été appelée (et éventuellement quels arguments ont été passés). Dans ces cas, un espion serait suffisant, et vous pouvez l'utiliser directement avec vi.spyOn()
(en savoir plus ici).
Cependant, les espions ne permettent que d'espionner les fonctions ; ils ne sont pas capables de modifier leur implémentation. Dans le cas où nous devons créer une version simulée (ou mockée) d'une fonction, nous pouvons utiliser vi.fn()
(en savoir plus ici).
Nous utilisons Tinyspy comme base pour le mocking de fonctions, mais nous avons notre propre habillage pour le rendre compatible avec jest
. vi.fn()
et vi.spyOn()
partagent les mêmes méthodes ; cependant, seul le résultat de vi.fn()
est invocable.
Exemple
import { afterEach, describe, expect, it, vi } from 'vitest';
const messages = {
items: [
{ message: 'Simple test message', from: 'Testman' },
// ...
],
getLatest, // peut aussi être un `accesseur ou mutateur, si supporté`
};
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 mocker des variables globales qui ne sont pas présentes avec jsdom
ou node
en utilisant la fonction utilitaire vi.stubGlobal
. Cela insérera la valeur de la variable globale dans un 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);
// maintenant vous pouvez y accéder en tant que `IntersectionObserver` ou `window.IntersectionObserver`
Modules
Les modules mockés simulent les bibliothèques tierces qui sont invoquées dans d'autres parties du code, vous permettant de tester les arguments, le résultat 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'automocking
Si votre code importe un module mocké, sans fichier __mocks__
associé ou factory
pour ce module, Vitest simulera le module lui-même en l'invoquant et en mockant chaque export.
Les principes suivants s'appliquent :
- Tous les tableaux seront vidés.
- Tous les types primitifs et collections seront conservés tels quels.
- Tous les objets seront clonés en profondeur.
- Toutes les instances de classes et leurs prototypes seront clonées en profondeur.
Modules virtuels
Vitest prend en charge le mocking des modules virtuels de Vite. Cela fonctionne différemment de la manière dont les modules virtuels sont traités dans Jest. Au lieu de passer virtual: true
à une fonction vi.mock
, vous devez signaler à Vite l'existence du module, sinon il échouera lors de l'analyse. Vous pouvez le faire de plusieurs manières :
- Fournir un alias
// vitest.config.js
export default {
test: {
alias: {
'$app/forms': resolve('./mocks/forms.js'),
},
},
};
- Fournir un plugin qui résout un module virtuel
// vitest.config.js
export default {
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 du mocking
Notez qu'il n'est pas possible de mocker les appels à des méthodes qui sont appelées depuis 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 mocker la méthode foo
depuis l'extérieur car elle est référencée directement. Ce code sera donc sans 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" que si elle est appelée 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" que si elle est appelée en dehors du module original
foo: () => 'mocked',
};
});
Vous pouvez confirmer ce comportement en implémentant directement la méthode foobar
:
// foobar.test.js
import * as mod from './foobar.js';
vi.spyOn(mod, 'foo');
// la fonction foo exportée référence la méthode mockée
mod.foobar(mod.foo);
// foobar.js
export function foo() {
return 'foo';
}
export function foobar(injectedFoo) {
return injectedFoo === foo; // false
}
C'est le comportement attendu. C'est généralement un signe de code de mauvaise qualité lorsque le mocking est utilisé de cette manière. Il est conseillé 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';
// récupérer 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
Le mocking du système de fichiers garantit que les tests ne dépendent pas du système de fichiers réel, ce qui les rend plus fiables et prévisibles. Cette isolation permet d'éviter les effets de bord des tests précédents. Elle permet de tester les conditions d'erreur et les cas limites qui pourraient être difficiles ou impossibles à reproduire avec un système de fichiers réel, tels que les problèmes de permissions, les scénarios de disque plein ou les erreurs de lecture/écriture.
Vitest ne fournit aucune API de mocking de système de fichiers nativement. Vous pouvez utiliser vi.mock
pour mocker le module fs
manuellement, mais cela est difficile à maintenir. Au lieu de cela, nous vous recommandons d'utiliser memfs
pour cette tâche. 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 fiable, évitant tout effet secondaire potentiel sur le système de fichiers réel.
Exemple
Pour rediriger automatiquement chaque appel au module 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 dans ce cas
// chaque export devrait être explicitement défini
const { fs } = require('memfs');
module.exports = fs;
// nous pouvons aussi utiliser `import`, mais dans ce cas
// chaque export devrait être explicitement défini
const { fs } = require('memfs');
module.exports = fs.promises;
// read-hello-world.js
import { readFileSync } from 'node:fs';
export function readHelloWorld(path) {
return readFileSync(path);
}
// hello-world.test.js
import { beforeEach, expect, it, vi } from 'vitest';
import { fs, vol } from 'memfs';
import { readHelloWorld } from './read-hello-world.js';
// indiquer à Vitest d'utiliser le mock fs du dossier __mocks__
// cela peut être fait dans un fichier de setup si fs doit toujours être mocké
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, le mocking des requêtes réseau est délicat ; les API web ne sont pas disponibles, nous avons donc besoin d'un moyen de simuler le comportement réseau. Nous recommandons Mock Service Worker pour y parvenir. Il vous permettra de mocker les requêtes réseau REST
et GraphQL
, et est agnostique au framework.
Mock Service Worker (MSW) fonctionne en interceptant les requêtes émises par vos tests, vous permettant 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, veuillez consulter leur introduction
Configuration
Vous pouvez l'utiliser comme ci-dessous dans votre fichier de setup
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { graphql, 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 graphqlHandlers = [
graphql.query('ListPosts', () => {
return HttpResponse.json({
data: { posts },
});
}),
];
const server = setupServer(...restHandlers, ...graphqlHandlers);
// Démarrage du serveur avant tous les tests
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));
// Fermeture du serveur après tous les tests
afterAll(() => server.close());
// Réinitialisation des gestionnaires après chaque test (important pour l'isolation des tests)
afterEach(() => server.resetHandlers());
La configuration du serveur avec
onUnhandleRequest: 'error'
garantit qu'une erreur est levée pour toute requête sans gestionnaire correspondant.
Plus
Il y a beaucoup plus à découvrir avec MSW. Vous pouvez accéder aux cookies et aux paramètres de requête, définir des réponses d'erreur mockées, et bien plus encore ! Pour voir tout ce que vous pouvez faire avec MSW, lisez leur documentation.
Temporisateurs
Lorsque nous testons du code qui implique des délais d'attente ou des intervalles, au lieu de laisser nos tests s'exécuter jusqu'à leur terme ou expirer, nous pouvons accélérer nos tests en utilisant des temporisateurs "factices" 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 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 mocker une classe entière avec un seul appel vi.fn
- puisque toutes les classes sont aussi des fonctions, cela fonctionne nativement. Notez que Vitest ne respecte pas actuellement 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';
}
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;
});
// notez que les méthodes statiques sont mockées directement sur la fonction,
// pas 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 de ces espions
Dog.prototype.speak = vi.fn(() => 'loud bark!');
Dog.prototype.feed = vi.fn();
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 attend une instance de la même interface :
// ./src/feed.ts
function feed(dog: Dog) {
// ...
}
// ./tests/dog.test.ts
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
) est déjà mockée :
const dog = new Dog('Cooper');
dog.speak(); // loud bark!
// vous pouvez utiliser les assertions intégrées pour vérifier la validité de l'appel
expect(dog.speak).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 mockée,
// il enveloppe toute fonction dans un type MockInstance<T>
// sans valider si la fonction est un mock
vi.mocked(dog.speak).mockReturnValue('woof woof');
dog.speak(); // woof woof
Pour mocker 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é mocké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 getters et les setters en utilisant la même approche.
Aide-mémoire
INFO
vi
dans les exemples ci-dessous est importé directement de vitest
. Vous pouvez également l'utiliser globalement, si vous définissez globals
à true
dans votre configuration.
Je veux…
Mocker des variables exportées
// some-path.js
export const getter = 'variable';
// some-path.test.ts
import * as exports from './some-path.js';
vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked');
Mocker une fonction exportée
- Exemple avec
vi.mock
:
WARNING
N'oubliez pas qu'un appel vi.mock
est remonté en haut du fichier. Il sera toujours exécuté avant toutes les importations.
// ./some-path.js
export function method() {}
import { method } from './some-path.js';
vi.mock('./some-path.js', () => ({
method: vi.fn(),
}));
- Exemple avec
vi.spyOn
:
import * as exports from './some-path.js';
vi.spyOn(exports, 'method').mockImplementation(() => {});
Mocker l'implémentation d'une classe exportée
- Exemple avec
vi.mock
et.prototype
:
// ./some-path.ts
export class SomeClass {}
import { SomeClass } from './some-path.js';
vi.mock(import('./some-path.js'), () => {
const SomeClass = vi.fn();
SomeClass.prototype.someMethod = vi.fn();
return { SomeClass };
});
// SomeClass.mock.instances contiendra l'instance de SomeClass
- Exemple avec
vi.spyOn
:
import * as mod from './some-path.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 :
// some-path.ts
export function useObject() {
return { method: () => true };
}
// useObject.js
import { useObject } from './some-path.js';
const obj = useObject();
obj.method();
// useObject.test.js
import { useObject } from './some-path.js';
vi.mock(import('./some-path.js'), () => {
let _cache;
const useObject = () => {
if (!_cache) {
_cache = {
method: vi.fn(),
};
}
// désormais, 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();
Mocker 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 espion
WARNING
N'oubliez pas que cela ne mocke 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.
Mocker la date actuelle
Pour mocker 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 successifs.
Notez que l'utilisation de vi.useFakeTimers
modifie également l'heure de Date
.
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// réinitialiser l'heure mockée
vi.useRealTimers();
Mocker une variable globale
Vous pouvez définir une variable globale en attribuant une valeur à globalThis
ou en utilisant la fonction utilitaire vi.stubGlobal
. Lorsque vous utilisez vi.stubGlobal
, elle ne sera pas automatiquement réinitialisée entre les tests successifs, 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');
Mocker import.meta.env
- Pour modifier une variable d'environnement, vous pouvez simplement lui attribuer une nouvelle valeur.
WARNING
La valeur de la variable d'environnement ne sera pas automatiquement réinitialisée entre les tests successifs.
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 la fonction 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('la valeur est restaurée avant l\'exécution d\'un autre test', () => {
expect(import.meta.env.VITE_ENV).toBe('test');
});
// vitest.config.ts
export default defineConfig({
test: {
unstubEnvs: true,
},
});