Skip to content
Vitest 0
Main Navigation GuideAPIConfigurationAvancé
1.6.1
0.34.6

Français

English
简体中文
繁體中文
Español
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Français

English
简体中文
繁體中文
Español
Русский
Português – Brasil
Deutsch
日本語
한국어
Italiano
Polski
Türkçe
čeština
magyar

Apparence

Sidebar Navigation

Guide

Pourquoi Vitest

Premiers pas

Fonctionnalités

Workspace

Interface de ligne de commande

Filtrage des tests

Couverture

Instantané (Snapshot)

Simulations

Tests de Typage

Interface utilisateur de Vitest

Mode Navigateur (expérimental)

Tests intégrés au code source

Contexte de Test

Environnement de test

Extension des vérificateurs (Matchers)

Intégrations pour IDE

Débogage

Comparaison avec d'autres outils de test

Guide de migration

Erreurs courantes

API

Référence de l'API de Test

Fonctions Mock

Vi

expect

expectTypeOf

assertType

Configuration

Configuration de Vitest

Sur cette page

Simulations ​

Lors de l'écriture de tests, il est fréquent de devoir créer une version simulée d'un service interne ou externe. C'est ce qu'on appelle communément le mocking. Vitest fournit des utilitaires pour vous aider, grâce à son outil vi. Vous pouvez import { vi } from 'vitest' ou y accéder globalement (lorsque la configuration globale est activée).

WARNING

N'oubliez pas de réinitialiser ou de restaurer les mocks avant ou après chaque test pour éviter les modifications d'état entre les exécutions ! Consultez la documentation de mockReset pour plus d'informations.

Si vous souhaitez explorer directement, consultez la section API, sinon, continuez votre lecture pour une exploration plus détaillée des simulations.

Dates ​

Il est parfois nécessaire de maîtriser la date pour garantir la cohérence des tests. Vitest utilise le package @sinonjs/fake-timers pour manipuler les temporisateurs, ainsi que la date système. Vous trouverez plus d'informations sur l'API spécifique ici.

Exemple ​

js
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(() => {
    // Indique à Vitest que nous utilisons un temps simulé
    vi.useFakeTimers();
  });

  afterEach(() => {
    // Restaure la date après chaque exécution de test
    vi.useRealTimers();
  });

  it('autorise les achats pendant les heures d'ouverture', () => {
    // Définit l'heure pendant les heures d'ouverture
    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('n\'autorise pas les achats en dehors des heures d'ouverture', () => {
    // Définit l'heure en dehors des heures d'ouverture
    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 distinctes : espionnage et mocking.

Parfois, il suffit de valider si une fonction spécifique a été appelée ou non (et éventuellement quels arguments ont été passés). Dans ce cas, un espion suffit, et vous pouvez l'utiliser directement avec vi.spyOn() (en savoir plus ici).

Cependant, les espions ne peuvent qu'observer les fonctions, ils ne peuvent pas modifier leur implémentation. Si nous devons créer une version simulée d'une fonction, nous pouvons utiliser vi.fn() (en savoir plus ici).

Nous utilisons Tinyspy comme base pour simuler des fonctions, mais nous avons notre propre wrapper pour le rendre compatible avec jest. vi.fn() et vi.spyOn() partagent les mêmes méthodes, cependant, seul le résultat de retour de vi.fn() est appelable.

Exemple ​

js
import { afterEach, describe, expect, it, vi } from 'vitest';

function getLatest(index = messages.items.length - 1) {
  return messages.items[index];
}

const messages = {
  items: [
    { message: 'Simple test message', from: 'Testman' },
    // ...
  ],
  getLatest, // peut également être un `getter ou setter si pris en charge`
};

describe('reading messages', () => {
  afterEach(() => {
    vi.restoreAllMocks();
  });

  it('devrait obtenir le dernier message avec un espion', () => {
    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('devrait obtenir le dernier message avec un 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);
  });
});

En savoir plus ​

  • Fonctions Mock de Jest

Variables Globales ​

Vous pouvez simuler des variables globales qui ne sont pas présentes avec jsdom ou node en utilisant l'assistant vi.stubGlobal. Il insérera la valeur de la variable globale dans l'objet globalThis.

ts
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 comme `IntersectionObserver` ou `window.IntersectionObserver`

Modules ​

Les modules simulés permettent d'observer les bibliothèques tierces, qui sont invoquées dans un autre code, afin de tester les arguments, la sortie, ou même de redéfinir leur implémentation.

Consultez la section API vi.mock() pour une description plus approfondie de l'API.

Algorithme d'automocking ​

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 règles suivantes s'appliquent :

  • Tous les tableaux seront réinitialisés
  • 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ées en profondeur

Exemple ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
import { Client } from 'pg';
import { failure, success } from './handlers.js';

// handlers
export function success(data) {}
export function failure(data) {}

// get todos
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('devrait renvoyer les éléments avec succès', 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('devrait lancer une erreur', 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 });
  });
});

Requêtes ​

La simulation des requêtes réseau est complexe car Vitest s'exécute dans Node ; les API web ne sont pas disponibles, nous avons donc besoin de quelque chose qui imitera le comportement du réseau pour nous. Nous recommandons Mock Service Worker pour cela. Il vous permettra de simuler les requêtes réseau REST et GraphQL, et est indépendant du framework.

Mock Service Worker (MSW) fonctionne en interceptant les requêtes que vos tests effectuent, 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 node-request-interceptor. Pour en savoir plus sur MSW, consultez leur introduction

Configuration ​

Vous pouvez l'utiliser comme ci-dessous dans votre fichier de configuration

js
import { afterAll, afterEach, beforeAll } from 'vitest';
import { setupServer } from 'msw/node';
import { graphql, rest } from 'msw';

const posts = [
  {
    userId: 1,
    id: 1,
    title: 'first post title',
    body: 'first post body',
  },
  // ...
];

export const restHandlers = [
  rest.get('https://rest-endpoint.example/path/to/posts', (req, res, ctx) => {
    return res(ctx.status(200), ctx.json(posts));
  }),
];

const graphqlHandlers = [
  graphql.query(
    'https://graphql-endpoint.example/api/v1/posts',
    (req, res, ctx) => {
      return res(ctx.data(posts));
    }
  ),
];

const server = setupServer(...restHandlers, ...graphqlHandlers);

// Démarre le serveur avant tous les tests
beforeAll(() => server.listen({ onUnhandledRequest: 'error' }));

// Ferme le serveur après tous les tests
afterAll(() => server.close());

// Réinitialise les 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 chaque fois qu'il y a une requête qui n'a pas de gestionnaire de requête correspondant.

Exemple ​

Nous avons un exemple de travail complet qui utilise MSW : React Testing with MSW.

Plus d'informations ​

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 simulées, et bien plus encore ! Pour voir tout ce que vous pouvez faire avec MSW, consultez leur documentation.

Temporisateurs ​

Lorsque nous testons du code qui implique des délais d'attente ou des intervalles, au lieu de laisser nos tests attendre 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 vi.useFakeTimers pour une description plus approfondie de l'API.

Exemple ​

js
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';

function executeAfterTwoHours(func) {
  setTimeout(func, 1000 * 60 * 60 * 2); // 2 hours
}

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('devrait exécuter la fonction', () => {
    executeAfterTwoHours(mock);
    vi.runAllTimers();
    expect(mock).toHaveBeenCalledTimes(1);
  });
  it('ne devrait pas exécuter la fonction', () => {
    executeAfterTwoHours(mock);
    // L'avancement de 2ms ne déclenchera pas la fonction
    vi.advanceTimersByTime(2);
    expect(mock).not.toHaveBeenCalled();
  });
  it('devrait exécuter toutes les minutes', () => {
    executeEveryMinute(mock);
    vi.advanceTimersToNextTimer();
    expect(mock).toHaveBeenCalledTimes(1);
    vi.advanceTimersToNextTimer();
    expect(mock).toHaveBeenCalledTimes(2);
  });
});

Mémo ​

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…

  • Espionner une method
ts
const instance = new SomeClass();
vi.spyOn(instance, 'method');
  • Simuler des variables exportées
js
// some-path.js
export const getter = 'variable';
ts
// some-path.test.ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'getter', 'get').mockReturnValue('mocked');
  • Simuler une fonction exportée

Exemple avec vi.mock :

ts
// ./some-path.js
export function method() {}
ts
import { method } from './some-path.js';

vi.mock('./some-path.js', () => ({
  method: vi.fn(),
}));

WARNING

N'oubliez pas que l'appel vi.mock est remonté (hoisted) en haut du fichier. Ne placez pas les appels vi.mock à l'intérieur de beforeEach : seul le premier appel simulera le module.

Exemple avec vi.spyOn :

ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'method').mockImplementation(() => {});
  • Simuler l'implémentation d'une classe exportée

Exemple avec vi.mock et prototype :

ts
// some-path.ts
export class SomeClass {}
ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn();
  SomeClass.prototype.someMethod = vi.fn();
  return { SomeClass };
});
// SomeClass.mock.instances contiendra des instances de SomeClass

Exemple avec vi.mock et valeur de retour :

ts
import { SomeClass } from './some-path.js';

vi.mock('./some-path.js', () => {
  const SomeClass = vi.fn(() => ({
    someMethod: vi.fn(),
  }));
  return { SomeClass };
});
// SomeClass.mock.results contiendra les objets retournés

Exemple avec vi.spyOn :

ts
import * as exports from './some-path.js';

vi.spyOn(exports, 'SomeClass').mockImplementation(() => {
  // selon ce qui vous convient parmi les exemples précédents
});
  • Espionner un objet retourné par une fonction

Exemple utilisant le cache :

ts
// some-path.ts
export function useObject() {
  return { method: () => true };
}
ts
// useObject.js
import { useObject } from './some-path.js';

const obj = useObject();
obj.method();
ts
// useObject.test.js
import { useObject } from './some-path.js';

vi.mock('./some-path.js', () => {
  let _cache;
  const useObject = () => {
    if (!_cache) {
      _cache = {
        method: vi.fn(),
      };
    }
    // Désormais, chaque fois que `useObject()` est appelé, il
    // renverra 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
ts
import { mocked, original } from './some-path.js';

vi.mock('./some-path.js', async () => {
  const mod = await vi.importActual<typeof import('./some-path.js')>(
    './some-path.js'
  );
  return {
    ...mod,
    mocked: vi.fn(),
  };
});
original(); // a le comportement d'origine
mocked(); // est une fonction espion
  • Simuler la date actuelle

Pour simuler l'heure de Date, vous pouvez utiliser la fonction d'assistance vi.setSystemTime. Cette valeur ne sera pas automatiquement réinitialisée entre les tests.

Notez que l'utilisation de vi.useFakeTimers modifie également l'heure de Date.

ts
const mockDate = new Date(2022, 0, 1);
vi.setSystemTime(mockDate);
const now = new Date();
expect(now.valueOf()).toBe(mockDate.valueOf());
// réinitialiser l'heure simulée
vi.useRealTimers();
  • Simuler une variable globale

Vous pouvez définir une variable globale en assignant une valeur à globalThis ou en utilisant la fonction d'assistance vi.stubGlobal. Lorsque vous utilisez vi.stubGlobal, la variable ne sera pas automatiquement réinitialisée entre les tests, sauf si vous activez l'option de configuration unstubGlobals ou si vous appelez vi.unstubAllGlobals.

ts
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. Cette valeur ne sera pas automatiquement réinitialisée entre les tests.

ts
import { beforeEach, expect, it } from 'vitest';

// vous pouvez la réinitialiser manuellement dans le hook `beforeEach`
const originalViteEnv = import.meta.env.VITE_ENV;

beforeEach(() => {
  import.meta.env.VITE_ENV = originalViteEnv;
});

it('modifie la valeur', () => {
  import.meta.env.VITE_ENV = 'staging';
  expect(import.meta.env.VITE_ENV).toBe('staging');
});

Si vous souhaitez réinitialiser automatiquement la valeur, vous pouvez utiliser la fonction d'assistance vi.stubEnv avec l'option de configuration unstubEnvs activée (ou appeler vi.unstubAllEnvs manuellement dans le hook beforeEach) :

ts
import { expect, it, vi } from 'vitest';

// avant l'exécution des tests, "VITE_ENV" vaut "test"
import.meta.env.VITE_ENV === 'test';

it('modifie la valeur', () => {
  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');
});
ts
// vitest.config.ts
export default {
  test: {
    unstubAllEnvs: true,
  },
};
Pager
Page précédenteInstantané (Snapshot)
Page suivanteTests de Typage

Publié sous la licence MIT.

Copyright (c) 2024 Mithril Contributors

https://v0.vitest.dev/guide/mocking

Publié sous la licence MIT.

Copyright (c) 2024 Mithril Contributors