Aller au contenu principal
Version : Suivant

Fonctions simulées

Les fonctions simulées sont également connues sous le nom d'« espions », car elles vous permettent d'espionner le comportement d'une fonction qui est appelée indirectement par un autre code, plutôt que de tester uniquement la sortie. Vous pouvez créer une fonction simulée avec jest.fn(). Si aucune implémentation n’est donnée, la fonction simulée retournera undefined lorsqu’elle est appelée.

info

Les exemples TypeScript de cette page ne fonctionneront comme documenté que si vous importez explicitement les API Jest :

import {expect, jest, test} from '@jest/globals';

Consult the Getting Started guide for details on how to setup Jest with TypeScript.

Méthodes


Référence

mockFn.getMockName()

Returns the mock name string set by calling .mockName().

mockFn.mock.calls

Un tableau contenant les arguments d'appel de tous les appels qui ont été faits à cette fonction simulée. Chaque élément du tableau est un tableau d'arguments qui ont été passés pendant l'appel.

Par exemple : Une fonction simulée f qui a été appelée deux fois, avec les arguments f('arg1', 'arg2'), puis avec les arguments f('arg3', 'arg4'), aurait un tableau mock.calls qui ressemble à ceci :

[
['arg1', 'arg2'],
['arg3', 'arg4'],
];

mockFn.mock.results

Un tableau contenant les résultats de tous les appels qui ont été faits à cette fonction simulée. Chaque entrée de ce tableau est un objet contenant une propriété type, et une propriété value. type sera l'un des suivants :

  • 'return' - Indique que l'appel s'est terminé en retournant normalement.
  • 'throw' - Indique que l'appel s'est terminé en levant une valeur.
  • 'incomplete' - Indique que l'appel n'est pas encore terminé. Cela se produit si vous testez le résultat à partir de la fonction simulée elle-même, ou à partir d'une fonction qui a été appelée par la fonction simulée.

La propriété value contient la valeur qui a été levée ou retournée. value est indéfinie lorsque type === 'incomplete'.

Par exemple : Une fonction simulée f qui a été appelée trois fois, renvoyant 'result1', lançant une erreur, puis renvoyant 'result2', aurait un tableau mock.results qui ressemble à ceci :

[
{
type: 'return',
value: 'result1',
},
{
type: 'throw',
value: {
/* Error instance */
},
},
{
type: 'return',
value: 'result2',
},
];

mockFn.mock.instances

Un tableau qui contient toutes les instances d'objet qui ont été instanciées à partir de cette fonction simulée en utilisant new.

Par exemple : Une fonction simulée qui a été instanciée deux fois aurait le tableau mock.instances suivant :

const mockFn = jest.fn();

const a = new mockFn();
const b = new mockFn();

mockFn.mock.instances[0] === a; // true
mockFn.mock.instances[1] === b; // true

mockFn.mock.contexts

Un tableau qui contient les contextes pour tous les appels de la fonction simulée.

Un contexte est la valeur this qu'une fonction reçoit lorsqu'elle est appelée. Le contexte peut être défini en utilisant Function.prototype.bind, Function.prototype.call ou Function.prototype.apply.

Par exemple :

const mockFn = jest.fn();

const boundMockFn = mockFn.bind(thisContext0);
boundMockFn('a', 'b');
mockFn.call(thisContext1, 'a', 'b');
mockFn.apply(thisContext2, ['a', 'b']);

mockFn.mock.contexts[0] === thisContext0; // true
mockFn.mock.contexts[1] === thisContext1; // true
mockFn.mock.contexts[2] === thisContext2; // true

mockFn.mock.lastCall

Un tableau contenant les arguments d'appel du dernier appel qui a été fait à cette fonction simulée. Si la fonction n'a pas été appelée, elle retournera undefined.

Par exemple : Une fonction simulée f qui a été appelée deux fois, avec les arguments f('arg1', 'arg2'), puis avec les arguments f('arg3', 'arg4'), aurait un tableau mock.lastCall qui ressemble à ceci :

['arg3', 'arg4'];

mockFn.mockClear()

Efface toutes les informations stockées dans les tableaux mockFn.mock.calls, mockFn.mock.instances, mockFn.mock.contexts et mockFn.mock.results. Souvent, cela est utile lorsque vous voulez nettoyer les données d'utilisation d'une simulation entre deux assertions.

Attention, mockFn.mockClear() remplacera mockFn.mock, et ne se contentera pas de réinitialiser les valeurs de ses propriétés ! Vous devriez donc éviter d'assigner mockFn.mock à d'autres variables, temporaires ou non, pour être sûr de ne pas accéder à des données périmées.

L'option de configuration clearMocks est disponible pour effacer les simulations automatiquement avant chaque test.

mockFn.mockReset()

Fait tout ce que mockFn.mockClear() fait, et supprime également toute valeur de retour ou d'implémentation simulées.

Ceci est utile lorsque vous voulez réinitialiser complètement une simulation à son état initial. (Notez que la réinitialisation d'un espion se traduira par une fonction sans valeur de retour).

The resetMocks configuration option is available to reset mocks automatically before each test.

mockFn.mockRestore()

Fait tout ce que mockFn.mockReset() fait, et restaure également l'implémentation originale (non simulée).

Ceci est utile lorsque vous voulez simuler des fonctions dans certains cas de test et restaurer l'implémentation originale dans d'autres.

Attention, mockFn.mockRestore() ne fonctionne que lorsque la simulation a été créée avec jest.spyOn(). Vous devez donc vous occuper vous-même de la restauration lorsque vous assignez manuellement jest.fn().

L'option de configuration restoreMocks est disponible pour restaurer les simulations automatiquement avant chaque test.

mockFn.mockImplementation(fn)

Accepte une fonction qui doit être utilisée comme l'implémentation de simulation. La simulation elle-même enregistrera toujours tous les appels qui entrent et les instances qui proviennent d'elle - la seule différence est que l'implémentation sera également exécutée lorsque la simulation est appelée.

astuce

jest.fn(implémentation) est un raccourci pour jest.fn().mockImplementation(implémentation).

const mockFn = jest.fn(scalar => 42 + scalar);

mockFn(0); // 42
mockFn(1); // 43

mockFn.mockImplementation(scalar => 36 + scalar);

mockFn(2); // 38
mockFn(3); // 39

.mockImplementation() peut également être utilisé pour simuler les constructeurs de classes :

SomeClass.js
module.exports = class SomeClass {
method(a, b) {}
};
SomeClass.test.js
const SomeClass = require('./SomeClass');

jest.mock('./SomeClass'); // cela se produit automatiquement avec l'auto-simulation

const mockMethod = jest.fn();
SomeClass.mockImplementation(() => {
return {
method: mockMethod,
};
});

const some = new SomeClass();
some.method('a', 'b');

console.log('Calls to method: ', mockMethod.mock.calls);

mockFn.mockImplementationOnce(fn)

Accepte une fonction qui sera utilisée comme une implémentation de simulation pour un appel à la fonction simulée. Peut être enchaîné de sorte que plusieurs appels de fonction produisent des résultats différents.

const mockFn = jest
.fn()
.mockImplementationOnce(cb => cb(null, true))
.mockImplementationOnce(cb => cb(null, false));

mockFn((err, val) => console.log(val)); // true
mockFn((err, val) => console.log(val)); // false

Lorsque la fonction simulée manque d'implémentations définies avec .mockImplementationOnce(), elle exécutera l'implémentation par défaut définie avec jest.fn(() => defaultValue) ou .mockImplementation(() => defaultValue) s'ils ont été appelés :

const mockFn = jest
.fn(() => 'par défaut')
.mockImplementationOnce(() => 'premier appel')
.mockImplementationOnce(() => 'second appel');

mockFn(); // 'premier appel'
mockFn(); // 'second appel'
mockFn(); // 'par défaut'
mockFn(); // 'par défaut'

mockFn.mockName(name)

Accepte une chaîne à utiliser dans le résultat du test à la place de 'jest.fn()' pour indiquer quelle fonction simulée est référencée.

Par exemple :

const mockFn = jest.fn().mockName('mockedFunction');

// mockFn();
expect(mockFn).toHaveBeenCalled();

Le résultat est cette erreur :

expect(mockedFunction).toHaveBeenCalled()

Expected number of calls: >= 1
Received number of calls: 0

mockFn.mockReturnThis()

Shorthand for:

jest.fn(function () {
return this;
});

mockFn.mockReturnValue(value)

Accepte une valeur qui sera retournée chaque fois que la fonction simulée est appelée.

const mock = jest.fn();

mock.mockReturnValue(42);
mock(); // 42

mock.mockReturnValue(43);
mock(); // 43

mockFn.mockReturnValueOnce(value)

Accepte une valeur qui sera retournée pour un appel à la fonction simulée. Peut être enchaîné de sorte que les appels successifs à la fonction simulée retournent des valeurs différentes. Quand il n'y a plus de valeurs mockReturnValueOnce à utiliser, les appels retourneront une valeur spécifiée par mockReturnValue.

const mockFn = jest
.fn()
.mockReturnValue('par défaut')
.mockReturnValueOnce('premier appel')
.mockReturnValueOnce('deuxième appel');

mockFn(); // 'premier appel'
mockFn(); // 'deuxième appel'
mockFn(); // 'par défaut'
mockFn(); // 'par défaut'

mockFn.mockResolvedValue(value)

Shorthand for:

jest.fn().mockImplementation(() => Promise.resolve(value));

Utile pour simuler les fonctions asynchrones dans les tests asynchrones :

test('async test', async () => {
const asyncMock = jest.fn().mockResolvedValue(43);

await asyncMock(); // 43
});

mockFn.mockResolvedValueOnce(value)

Shorthand for:

jest.fn().mockImplementationOnce(() => Promise.resolve(value));

Utile pour résoudre des valeurs différentes sur plusieurs appels asynchrones :

test('async test', async () => {
const asyncMock = jest
.fn()
.mockResolvedValue('par défaut')
.mockResolvedValueOnce('premier appel')
.mockResolvedValueOnce('deuxième appel');

await asyncMock(); // 'premier appel'
await asyncMock(); // 'deuxième appel'
await asyncMock(); // 'par défaut'
await asyncMock(); // 'par défaut'
});

mockFn.mockRejectedValue(value)

Shorthand for:

jest.fn().mockImplementation(() => Promise.reject(value));

Utile pour créer des fonctions simulées asynchrones qui seront toujours rejetées :

test('async test', async () => {
const asyncMock = jest
.fn()
.mockRejectedValue(new Error('Async error message'));

await asyncMock(); // lève 'Async error message'
});

mockFn.mockRejectedValueOnce(value)

Shorthand for:

jest.fn().mockImplementationOnce(() => Promise.reject(value));

Utile avec .mockResolvedValueOnce() ou pour rejeter avec des exceptions différentes sur plusieurs appels asynchrones :

test('async test', async () => {
const asyncMock = jest
.fn()
.mockResolvedValueOnce('premier appel')
.mockRejectedValueOnce(new Error('Async error message'));

await asyncMock(); // 'premier appel'
await asyncMock(); // lève 'Async error message'
});

mockFn.withImplementation(fn, callback)

Accepts a function which should be temporarily used as the implementation of the mock while the callback is being executed.

test('test', () => {
const mock = jest.fn(() => 'outside callback');

mock.withImplementation(
() => 'inside callback',
() => {
mock(); // 'inside callback'
},
);

mock(); // 'outside callback'
});

mockFn.withImplementation can be used regardless of whether or not the callback is asynchronous (returns a thenable). If the callback is asynchronous a promise will be returned. Awaiting the promise will await the callback and reset the implementation.

test('async test', async () => {
const mock = jest.fn(() => 'outside callback');

// We await this call since the callback is async
await mock.withImplementation(
() => 'inside callback',
async () => {
mock(); // 'inside callback'
},
);

mock(); // 'outside callback'
});

Utilisation de TypeScript

info

Les exemples TypeScript de cette page ne fonctionneront comme documenté que si vous importez explicitement les API Jest :

import {expect, jest, test} from '@jest/globals';

Consult the Getting Started guide for details on how to setup Jest with TypeScript.

jest.fn(implementation?)

Les typages simulés corrects seront déduits, si l'implémentation est passée à la fonction jest.fn(). Il existe de nombreux cas d'utilisation où l'implémentation est omise. Pour assurer la sécurité des types, vous pouvez passer un argument de type générique (voir aussi les exemples ci-dessus pour plus de référence) :

import {expect, jest, test} from '@jest/globals';
import type add from './add';
import calculate from './calc';

test('calculate appelle add', () => {
// Crée une nouvelle simulation qui peut être utilisé à la place de `add`.
const mockAdd = jest.fn<typeof add>();

// `.mockImplementation()` peut maintenant déduire que `a` et `b` sont des `number`
// et que la valeur renvoyée est un `number`.
mockAdd.mockImplementation((a, b) => {
// Oui, cette fonction simulée ajoute toujours deux nombres mais imaginez que
// c'est une fonction complexe que nous simulons.
return a + b
});

// `mockAdd` est correctement typé et donc accepté par tout
// ce que requiert `add`.
calculate(mockAdd, 1, 2);

expect(mockAdd).toHaveBeenCalledTimes(1);
expect(mockAdd).toHaveBeenCalledWith(1, 2);
});

jest.Mock<T>

Constructs the type of a mock function, e.g. the return type of jest.fn(). It can be useful if you have to defined a recursive mock function:

import {jest} from '@jest/globals';

const sumRecursively: jest.Mock<(value: number) => number> = jest.fn(value => {
if (value === 0) {
return 0;
} else {
return value + fn(value - 1);
}
});

jest.Mocked<Source>

The jest.Mocked<Source> utility type returns the Source type wrapped with type definitions of Jest mock function.

import {expect, jest, test} from '@jest/globals';
import type {fetch} from 'node-fetch';

jest.mock('node-fetch');

let mockedFetch: jest.Mocked<typeof fetch>;

afterEach(() => {
mockedFetch.mockClear();
});

test('makes correct call', () => {
mockedFetch = getMockedFetch();
// ...
});

test('returns correct data', () => {
mockedFetch = getMockedFetch();
// ...
});

Types of classes, functions or objects can be passed as type argument to jest.Mocked<Source>. If you prefer to constrain the input type, use: jest.MockedClass<Source>, jest.MockedFunction<Source> or jest.MockedObject<Source>.

jest.mocked(source, options?)

The mocked() helper method wraps types of the source object and its deep nested members with type definitions of Jest mock function. You can pass {shallow: true} as the options argument to disable the deeply mocked behavior.

Returns the source object.

song.ts
export const song = {
one: {
more: {
time: (t: number) => {
return t;
},
},
},
};
song.test.ts
import {expect, jest, test} from '@jest/globals';
import {song} from './song';

jest.mock('./song');
jest.spyOn(console, 'log');

const mockedSong = jest.mocked(song);
// or through `jest.Mocked<Source>`
// const mockedSong = song as jest.Mocked<typeof song>;

test('deep method is typed correctly', () => {
mockedSong.one.more.time.mockReturnValue(12);

expect(mockedSong.one.more.time(10)).toBe(12);
expect(mockedSong.one.more.time.mock.calls).toHaveLength(1);
});

test('direct usage', () => {
jest.mocked(console.log).mockImplementation(() => {
return;
});

console.log('one more time');

expect(jest.mocked(console.log).mock.calls).toHaveLength(1);
});

jest.Spied<Source>

Constructs the type of a spied class or function (i.e. the return type of jest.spyOn()).

__utils__/setDateNow.ts
import {jest} from '@jest/globals';

export function setDateNow(now: number): jest.Spied<typeof Date.now> {
return jest.spyOn(Date, 'now').mockReturnValue(now);
}
import {afterEach, expect, jest, test} from '@jest/globals';
import {setDateNow} from './__utils__/setDateNow';

let spiedDateNow: jest.Spied<typeof Date.now> | undefined = undefined;

afterEach(() => {
spiedDateNow?.mockReset();
});

test('renders correctly with a given date', () => {
spiedDateNow = setDateNow(1482363367071);
// ...

expect(spiedDateNow).toHaveBeenCalledTimes(1);
});

Types of a class or function can be passed as type argument to jest.Spied<Source>. If you prefer to constrain the input type, use: jest.SpiedClass<Source> or jest.SpiedFunction<Source>.

Use jest.SpiedGetter<Source> or jest.SpiedSetter<Source> to create the type of a spied getter or setter respectively.