Skip to content
Vitest 1
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

Espace de travail

Interface de ligne de commande

Filtrage des tests

Reporters

Couverture

Instantané (Snapshot)

Simulations

Tests de Type

Interface utilisateur de Vitest

Mode Navigateur

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

Améliorer les performances

API

Référence de l'API de Test

Fonctions Mock

Vi

expect

expectTypeOf

assert

assertType

Configuration

Configuration du fichier Vitest

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 principes suivants s'appliquent :

  • Tous les tableaux seront vidés
  • Tous les types primitifs et les collections resteront inchangés
  • Tous les objets seront profondément clonés
  • Toutes les instances de classes et leurs prototypes seront profondément clonés

Modules Virtuels ​

Vitest prend en charge le mocking 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 transmettre virtual: true à une fonction vi.mock, vous devez indiquer à Vite que le module existe, sinon l'analyse échouera. Vous pouvez le faire de plusieurs manières :

  1. Fournir un alias
ts
// vitest.config.js
export default {
  test: {
    alias: {
      '$app/forms': resolve('./mocks/forms.js'),
    },
  },
};
  1. Fournir un plugin qui résout un module virtuel
ts
// 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, ils seront tous affectés par vi.mock. Veillez donc à utiliser des identifiants uniques.

Pièges du Mocking ​

Attention, il n'est pas possible de mocker les appels aux méthodes qui sont appelées à l'intérieur d'autres méthodes du même fichier. Par exemple, dans ce code :

ts
export function foo() {
  return 'foo';
}

export function foobar() {
  return `${foo()}bar`;
}

Il n'est pas possible de mocker 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 foo à l'intérieur de foobar (mais il affectera l'appel foo dans d'autres modules) :

ts
import { vi } from 'vitest';
import * as mod from './foobar.js';

// this will only affect "foo" outside of the original module
vi.spyOn(mod, 'foo');
vi.mock('./foobar.js', async importOriginal => {
  return {
    ...(await importOriginal<typeof import('./foobar.js')>()),
    // this will only affect "foo" outside of the original module
    foo: () => 'mocked',
  };
});

Vous pouvez confirmer ce comportement en fournissant directement l'implémentation à la méthode foobar :

ts
// foobar.test.js
import * as mod from './foobar.js';

vi.spyOn(mod, 'foo');

// exported foo references mocked method
mod.foobar(mod.foo);
ts
// 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 mauvais code lorsque le mocking est utilisé 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 ​

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 @mswjs/interceptors. 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 { HttpResponse, graphql, http } 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é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 ​

  1. 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.

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

vi.mock('./some-path.js', () => ({
  method: vi.fn(),
}));
  1. 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 ​

  1. 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
  1. 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
  1. 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 ​

  1. 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 importOriginal => {
  const mod = await importOriginal<typeof import('./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 ​

  1. Pour modifier une variable d'environnement, il suffit de lui attribuer une nouvelle valeur.

WARNING

La valeur de la variable d'environnement ne sera pas automatiquement réinitialisée entre les différents 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');
});
  1. Si vous souhaitez réinitialiser automatiquement les valeurs, vous pouvez utiliser l'assistant vi.stubEnv avec l'option de configuration unstubEnvs activée (ou appeler vi.unstubAllEnvs](/api/vi#vi-unstuballenvs) manuellement dans un 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 Type

Publié sous la licence MIT.

Copyright (c) 2024 Mithril Contributors

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

Publié sous la licence MIT.

Copyright (c) 2024 Mithril Contributors