Aller au contenu principal
Version : Suivant

Expect

Traduction Bêta Non Officielle

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

Lorsque vous écrivez des tests, vous devez souvent vérifier que les valeurs satisfont certaines conditions. expect vous donne accès à plusieurs "matchers" permettant de valider différents aspects.

astuce

Pour des matchers Jest supplémentaires maintenus par la communauté, consultez jest-extended.

Traduction Bêta Non Officielle

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

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';

Consultez le guide Premiers pas pour plus de détails sur la configuration de Jest avec TypeScript.

Référence


Expect

expect(value)

La fonction expect est utilisée chaque fois que vous souhaitez tester une valeur. Vous appellerez rarement expect par elle-même. À la place, vous utiliserez expect avec une fonction "matcher" pour vérifier une propriété d'une valeur.

Un exemple rend cela plus clair. Imaginons une méthode bestLaCroixFlavor() censée retourner la chaîne 'grapefruit'. Voici comment vous la testeriez :

test('the best flavor is grapefruit', () => {
expect(bestLaCroixFlavor()).toBe('grapefruit');
});

Ici, toBe est la fonction matcher. De nombreux matchers différents, documentés ci-dessous, vous aident à tester divers éléments.

L'argument de expect doit être la valeur produite par votre code, tandis que l'argument du matcher doit être la valeur correcte. Si vous les inversez, vos tests fonctionneront mais les messages d'erreur sembleront étranges.

Modificateurs

.not

Si vous savez comment tester quelque chose, .not permet de tester son contraire. Par exemple, ce code vérifie que la meilleure saveur La Croix n'est pas la noix de coco :

test('the best flavor is not coconut', () => {
expect(bestLaCroixFlavor()).not.toBe('coconut');
});

.resolves

Utilisez resolves pour extraire la valeur d'une promesse tenue, afin d'enchaîner d'autres matchers. Si la promesse est rejetée, l'assertion échoue.

Par exemple, ce code teste que la promesse est résolue avec la valeur 'lemon' :

test('resolves to lemon', () => {
// make sure to add a return statement
return expect(Promise.resolve('lemon')).resolves.toBe('lemon');
});
note

Comme vous testez toujours des promesses, le test reste asynchrone. Vous devrez donc demander à Jest d'attendre en retournant l'assertion déballée.

Vous pouvez aussi utiliser async/await avec .resolves :

test('resolves to lemon', async () => {
await expect(Promise.resolve('lemon')).resolves.toBe('lemon');
await expect(Promise.resolve('lemon')).resolves.not.toBe('octopus');
});

.rejects

Utilisez .rejects pour extraire la raison d'un rejet de promesse afin d'enchaîner d'autres matchers. Si la promesse est tenue, l'assertion échoue.

Par exemple, ce code teste que la promesse est rejetée avec la raison 'octopus' :

test('rejects to octopus', () => {
// make sure to add a return statement
return expect(Promise.reject(new Error('octopus'))).rejects.toThrow(
'octopus',
);
});
note

Comme vous testez toujours des promesses, le test reste asynchrone. Vous devrez donc demander à Jest d'attendre en retournant l'assertion déballée.

Vous pouvez aussi utiliser async/await avec .rejects :

test('rejects to octopus', async () => {
await expect(Promise.reject(new Error('octopus'))).rejects.toThrow('octopus');
});

Matchers

.toBe(value)

Utilisez .toBe pour comparer des valeurs primitives ou vérifier l'identité référentielle d'instances d'objets. Il utilise Object.is pour comparer les valeurs, ce qui est préférable au strict égal === pour les tests.

Par exemple, ce code validera certaines propriétés de l'objet can :

const can = {
name: 'pamplemousse',
ounces: 12,
};

describe('the can', () => {
test('has 12 ounces', () => {
expect(can.ounces).toBe(12);
});

test('has a sophisticated name', () => {
expect(can.name).toBe('pamplemousse');
});
});

N'utilisez pas .toBe avec des nombres à virgule flottante. Par exemple, en JavaScript, 0.2 + 0.1 n'est pas strictement égal à 0.3 à cause des arrondis. Pour les nombres flottants, préférez .toBeCloseTo.

Bien que le matcher .toBe vérifie l'identité référentielle, il rapporte une comparaison approfondie des valeurs en cas d'échec de l'assertion. Si les différences entre les propriétés ne vous aident pas à comprendre pourquoi un test échoue, surtout si le rapport est volumineux, vous pouvez déplacer la comparaison dans la fonction expect. Par exemple, pour vérifier si des éléments sont la même instance :

  • remplacez expect(received).toBe(expected) par expect(Object.is(received, expected)).toBe(true)

  • remplacez expect(received).not.toBe(expected) par expect(Object.is(received, expected)).toBe(false)

.toHaveBeenCalled()

Utilisez .toHaveBeenCalled pour vérifier qu'une fonction mock a été appelée.

Par exemple, supposons que vous ayez une fonction drinkAll(drink, flavour) qui applique une fonction drink à toutes les boissons disponibles. Vous pourriez vouloir vérifier que drink est bien appelée. Voici comment vous pourriez écrire ce test :

function drinkAll(callback, flavour) {
if (flavour !== 'octopus') {
callback(flavour);
}
}

describe('drinkAll', () => {
test('drinks something lemon-flavoured', () => {
const drink = jest.fn();
drinkAll(drink, 'lemon');
expect(drink).toHaveBeenCalled();
});

test('does not drink something octopus-flavoured', () => {
const drink = jest.fn();
drinkAll(drink, 'octopus');
expect(drink).not.toHaveBeenCalled();
});
});

.toHaveBeenCalledTimes(number)

Utilisez .toHaveBeenCalledTimes pour vérifier qu'une fonction mock a été appelée un nombre précis de fois.

Par exemple, supposons que vous ayez une fonction drinkEach(drink, Array<flavor>) qui prend une fonction drink et l'applique à un tableau de boissons. Vous pourriez vouloir vérifier que la fonction drink est appelée exactement un certain nombre de fois. Voici comment vous pourriez écrire ce test :

test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);
});

.toHaveBeenCalledWith(arg1, arg2, ...)

Utilisez .toHaveBeenCalledWith pour vérifier qu'une fonction mock a été appelée avec des arguments spécifiques. Les arguments sont vérifiés avec le même algorithme que .toEqual.

Par exemple, supposons que vous puissiez enregistrer une boisson avec une fonction register, et que applyToAll(f) devrait appliquer la fonction f à toutes les boissons enregistrées. Pour vérifier cela, vous pourriez écrire :

test('registration applies correctly to orange La Croix', () => {
const beverage = new LaCroix('orange');
register(beverage);
const f = jest.fn();
applyToAll(f);
expect(f).toHaveBeenCalledWith(beverage);
});

.toHaveBeenLastCalledWith(arg1, arg2, ...)

Pour une fonction mock, utilisez .toHaveBeenLastCalledWith pour tester les arguments de son dernier appel. Par exemple, supposons que vous ayez une fonction applyToAllFlavors(f) qui applique f à plusieurs saveurs, et que vous vouliez vérifier que la dernière saveur traitée est 'mango'. Vous pourriez écrire :

test('applying to all flavors does mango last', () => {
const drink = jest.fn();
applyToAllFlavors(drink);
expect(drink).toHaveBeenLastCalledWith('mango');
});

.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)

Pour une fonction mock, utilisez .toHaveBeenNthCalledWith pour tester les arguments de son n-ième appel. Par exemple, supposons que vous ayez une fonction drinkEach(drink, Array<flavor>) qui applique f à plusieurs saveurs, et que vous vouliez vérifier que la première saveur traitée est 'lemon' et la seconde 'octopus'. Vous pourriez écrire :

test('drinkEach drinks each drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenNthCalledWith(1, 'lemon');
expect(drink).toHaveBeenNthCalledWith(2, 'octopus');
});
note

Le nième argument doit être un entier positif commençant à 1.

.toHaveReturned()

Pour une fonction mock, utilisez .toHaveReturned pour vérifier qu'elle a renvoyé une valeur (sans lever d'erreur) au moins une fois. Par exemple, supposons que vous ayez un mock drink qui renvoie true. Vous pourriez écrire :

test('drinks returns', () => {
const drink = jest.fn(() => true);

drink();

expect(drink).toHaveReturned();
});

.toHaveReturnedTimes(number)

Utilisez .toHaveReturnedTimes pour vérifier qu'une fonction mock a renvoyé une valeur (sans lever d'erreur) un nombre exact de fois. Les appels ayant levé une erreur ne sont pas comptabilisés.

Par exemple, supposons que vous ayez un mock drink qui renvoie true. Vous pourriez écrire :

test('drink returns twice', () => {
const drink = jest.fn(() => true);

drink();
drink();

expect(drink).toHaveReturnedTimes(2);
});

.toHaveReturnedWith(value)

Utilisez .toHaveReturnedWith pour vérifier qu'une fonction mock a renvoyé une valeur spécifique.

Par exemple, imaginez que vous ayez une simulation drink qui retourne le nom de la boisson consommée. Vous pouvez écrire :

test('drink returns La Croix', () => {
const beverage = {name: 'La Croix'};
const drink = jest.fn(beverage => beverage.name);

drink(beverage);

expect(drink).toHaveReturnedWith('La Croix');
});

.toHaveLastReturnedWith(value)

Utilisez .toHaveLastReturnedWith pour vérifier la valeur spécifique qu'une fonction simulée a retournée lors de son dernier appel. Si le dernier appel a généré une erreur, ce matcher échouera quelle que soit la valeur attendue fournie.

Par exemple, imaginez que vous ayez une simulation drink qui retourne le nom de la boisson consommée. Vous pouvez écrire :

test('drink returns La Croix (Orange) last', () => {
const beverage1 = {name: 'La Croix (Lemon)'};
const beverage2 = {name: 'La Croix (Orange)'};
const drink = jest.fn(beverage => beverage.name);

drink(beverage1);
drink(beverage2);

expect(drink).toHaveLastReturnedWith('La Croix (Orange)');
});

.toHaveNthReturnedWith(nthCall, value)

Utilisez .toHaveNthReturnedWith pour vérifier la valeur spécifique qu'une fonction simulée a retournée lors de son n-ième appel. Si le n-ième appel a généré une erreur, ce matcher échouera quelle que soit la valeur attendue fournie.

Par exemple, imaginez que vous ayez une simulation drink qui retourne le nom de la boisson consommée. Vous pouvez écrire :

test('drink returns expected nth calls', () => {
const beverage1 = {name: 'La Croix (Lemon)'};
const beverage2 = {name: 'La Croix (Orange)'};
const drink = jest.fn(beverage => beverage.name);

drink(beverage1);
drink(beverage2);

expect(drink).toHaveNthReturnedWith(1, 'La Croix (Lemon)');
expect(drink).toHaveNthReturnedWith(2, 'La Croix (Orange)');
});
note

Le nième argument doit être un entier positif commençant à 1.

.toHaveLength(number)

Utilisez .toHaveLength pour vérifier qu'un objet possède une propriété .length ayant une valeur numérique spécifique.

Particulièrement utile pour contrôler la taille des tableaux ou des chaînes de caractères.

expect([1, 2, 3]).toHaveLength(3);
expect('abc').toHaveLength(3);
expect('').not.toHaveLength(5);

.toHaveProperty(keyPath, value?)

Utilisez .toHaveProperty pour vérifier l'existence d'une propriété à l'emplacement keyPath d'un objet. Pour des propriétés profondément imbriquées, utilisez la notation par points ou un tableau de clés.

L'argument optionnel value permet de comparer la valeur de la propriété (avec égalité récursive comme le matcher toEqual).

L'exemple suivant contient un objet houseForSale avec des propriétés imbriquées. Nous utilisons toHaveProperty pour vérifier l'existence et les valeurs de diverses propriétés.

// Object containing house features to be tested
const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
area: 20,
wallColor: 'white',
'nice.oven': true,
},
livingroom: {
amenities: [
{
couch: [
['large', {dimensions: [20, 20]}],
['small', {dimensions: [10, 10]}],
],
},
],
},
'ceiling.height': 2,
};

test('this house has my desired features', () => {
// Example Referencing
expect(houseForSale).toHaveProperty('bath');
expect(houseForSale).toHaveProperty('bedrooms', 4);

expect(houseForSale).not.toHaveProperty('pool');

// Deep referencing using dot notation
expect(houseForSale).toHaveProperty('kitchen.area', 20);
expect(houseForSale).toHaveProperty('kitchen.amenities', [
'oven',
'stove',
'washer',
]);

expect(houseForSale).not.toHaveProperty('kitchen.open');

// Deep referencing using an array containing the keyPath
expect(houseForSale).toHaveProperty(['kitchen', 'area'], 20);
expect(houseForSale).toHaveProperty(
['kitchen', 'amenities'],
['oven', 'stove', 'washer'],
);
expect(houseForSale).toHaveProperty(['kitchen', 'amenities', 0], 'oven');
expect(houseForSale).toHaveProperty(
'livingroom.amenities[0].couch[0][1].dimensions[0]',
20,
);
expect(houseForSale).toHaveProperty(['kitchen', 'nice.oven']);
expect(houseForSale).not.toHaveProperty(['kitchen', 'open']);

// Referencing keys with dot in the key itself
expect(houseForSale).toHaveProperty(['ceiling.height'], 'tall');
});

.toBeCloseTo(number, numDigits?)

Utilisez toBeCloseTo pour comparer des nombres à virgule flottante avec une égalité approximative.

L'argument optionnel numDigits limite le nombre de chiffres à vérifier après la virgule. Par défaut (2), le critère est Math.abs(expected - received) < 0.005 (soit 10 ** -2 / 2).

Les comparaisons intuitives échouent souvent à cause des erreurs d'arrondi dans la représentation binaire des valeurs décimales. Par exemple, ce test échoue :

test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBe(0.3); // Fails!
});

Il échoue car en JavaScript, 0.2 + 0.1 vaut en réalité 0.30000000000000004.

Exemple avec une précision de 5 chiffres :

test('adding works sanely with decimals', () => {
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});

Comme toBeCloseTo résout spécifiquement les erreurs de virgule flottante, il ne prend pas en charge les très grands entiers.

.toBeDefined()

Utilisez .toBeDefined pour vérifier qu'une variable n'est pas undefined. Par exemple, pour vérifier qu'une fonction fetchNewFlavorIdea() retourne bien quelque chose :

test('there is a new flavor idea', () => {
expect(fetchNewFlavorIdea()).toBeDefined();
});

Vous pourriez écrire expect(fetchNewFlavorIdea()).not.toBe(undefined), mais il est préférable d'éviter de référencer directement undefined.

.toBeFalsy()

Utilisez .toBeFalsy lorsque vous voulez vérifier qu'une valeur est fausse dans un contexte booléen, peu importe sa nature. Par exemple, supposons un code applicatif comme :

drinkSomeLaCroix();
if (!getErrors()) {
drinkMoreLaCroix();
}

Vous ne vous souciez peut-être pas de ce que retourne getErrors spécifiquement - cela pourrait être false, null ou 0, et votre code fonctionnerait quand même. Donc si vous voulez tester qu'il n'y a pas d'erreurs après avoir bu du La Croix, vous pourriez écrire :

test('drinking La Croix does not lead to errors', () => {
drinkSomeLaCroix();
expect(getErrors()).toBeFalsy();
});

En JavaScript, il existe six valeurs "falsy" (fausses dans un contexte booléen) : false, 0, '', null, undefined et NaN. Tout le reste est "truthy" (vrai dans un contexte booléen).

.toBeGreaterThan(number | bigint)

Utilisez toBeGreaterThan pour vérifier received > expected avec des nombres ou des entiers de grande taille (bigint). Par exemple, testez que ouncesPerCan() retourne une valeur supérieure à 10 onces :

test('ounces per can is more than 10', () => {
expect(ouncesPerCan()).toBeGreaterThan(10);
});

.toBeGreaterThanOrEqual(number | bigint)

Utilisez toBeGreaterThanOrEqual pour vérifier received >= expected avec des nombres ou des entiers de grande taille (bigint). Par exemple, testez que ouncesPerCan() retourne une valeur d'au moins 12 onces :

test('ounces per can is at least 12', () => {
expect(ouncesPerCan()).toBeGreaterThanOrEqual(12);
});

.toBeLessThan(number | bigint)

Utilisez toBeLessThan pour vérifier received < expected avec des nombres ou des entiers de grande taille (bigint). Par exemple, testez que ouncesPerCan() retourne une valeur inférieure à 20 onces :

test('ounces per can is less than 20', () => {
expect(ouncesPerCan()).toBeLessThan(20);
});

.toBeLessThanOrEqual(number | bigint)

Utilisez toBeLessThanOrEqual pour vérifier received <= expected avec des nombres ou des entiers de grande taille (bigint). Par exemple, testez que ouncesPerCan() retourne une valeur d'au plus 12 onces :

test('ounces per can is at most 12', () => {
expect(ouncesPerCan()).toBeLessThanOrEqual(12);
});

.toBeInstanceOf(Class)

Utilisez .toBeInstanceOf(Class) pour vérifier qu'un objet est une instance d'une classe. Ce matcher utilise instanceof en arrière-plan.

class A {}

expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
expect(new A()).toBeInstanceOf(Function); // throws

.toBeNull()

.toBeNull() équivaut à .toBe(null) mais les messages d'erreur sont plus explicites. Utilisez donc .toBeNull() quand vous voulez vérifier qu'une valeur est null.

function bloop() {
return null;
}

test('bloop returns null', () => {
expect(bloop()).toBeNull();
});

.toBeTruthy()

Utilisez .toBeTruthy quand la valeur exacte ne vous importe pas mais que vous voulez vous assurer qu'elle est évaluée à true dans un contexte booléen. Par exemple, supposons que vous ayez un code applicatif comme :

drinkSomeLaCroix();
if (thirstInfo()) {
drinkMoreLaCroix();
}

Vous ne vous souciez peut-être pas de ce que retourne thirstInfo spécifiquement - cela pourrait être true ou un objet complexe, et votre code fonctionnerait quand même. Donc si vous voulez tester que thirstInfo sera truthy après avoir bu du La Croix, vous pourriez écrire :

test('drinking La Croix leads to having thirst info', () => {
drinkSomeLaCroix();
expect(thirstInfo()).toBeTruthy();
});

En JavaScript, il existe six valeurs "falsy" (fausses dans un contexte booléen) : false, 0, '', null, undefined et NaN. Tout le reste est "truthy" (vrai dans un contexte booléen).

.toBeUndefined()

Utilisez .toBeUndefined pour vérifier qu'une variable est undefined. Par exemple, si vous voulez vérifier qu'une fonction bestDrinkForFlavor(flavor) retourne undefined pour la saveur 'octopus', car il n'existe pas de bonne boisson au goût de poulpe :

test('the best drink for octopus flavor is undefined', () => {
expect(bestDrinkForFlavor('octopus')).toBeUndefined();
});

Vous pourriez écrire expect(bestDrinkForFlavor('octopus')).toBe(undefined), mais c'est une meilleure pratique d'éviter de faire référence directement à undefined dans votre code.

.toBeNaN()

Utilisez .toBeNaN pour vérifier qu'une valeur est NaN.

test('passes when value is NaN', () => {
expect(NaN).toBeNaN();
expect(1).not.toBeNaN();
});

.toContain(item)

Utilisez .toContain pour vérifier qu'un élément est présent dans un tableau. Pour tester les éléments du tableau, ce matcher utilise === (égalité stricte). .toContain peut aussi vérifier si une chaîne est une sous-chaîne d'une autre chaîne.

Par exemple, si getAllFlavors() retourne un tableau de saveurs et que vous voulez vous assurer que lime (citron vert) y figure, vous pouvez écrire :

test('the flavor list contains lime', () => {
expect(getAllFlavors()).toContain('lime');
});

Ce matcher accepte aussi d'autres itérables comme les chaînes, les ensembles (Set), les NodeList et les collections HTML.

.toContainEqual(item)

Utilisez .toContainEqual lorsque vous souhaitez vérifier qu'un élément avec une structure et des valeurs spécifiques est contenu dans un tableau. Pour tester les éléments du tableau, ce matcher vérifie récursivement l'égalité de tous les champs, plutôt que l'identité objet.

describe('my beverage', () => {
test('is delicious and not sour', () => {
const myBeverage = {delicious: true, sour: false};
expect(myBeverages()).toContainEqual(myBeverage);
});
});

.toEqual(value)

Utilisez .toEqual pour comparer récursivement toutes les propriétés des instances d'objets (également appelée égalité "profonde"). Il utilise Object.is pour comparer les valeurs primitives, ce qui est encore plus efficace pour les tests que l'opérateur d'égalité stricte ===.

Par exemple, .toEqual et .toBe se comportent différemment dans cette suite de tests, donc tous les tests passent :

const can1 = {
flavor: 'grapefruit',
ounces: 12,
};
const can2 = {
flavor: 'grapefruit',
ounces: 12,
};

describe('the La Croix cans on my desk', () => {
test('have all the same properties', () => {
expect(can1).toEqual(can2);
});
test('are not the exact same can', () => {
expect(can1).not.toBe(can2);
});
});
astuce

toEqual ignore les clés d'objet avec des propriétés undefined, les éléments de tableau undefined, la sparsité des tableaux ou les incohérences de type d'objet. Pour en tenir compte, utilisez plutôt .toStrictEqual.

info

.toEqual n'effectuera pas de vérification d'égalité profonde pour deux erreurs. Seule la propriété message d'une Error est considérée pour l'égalité. Il est recommandé d'utiliser le matcher .toThrow pour tester les erreurs.

Si les différences entre propriétés ne vous aident pas à comprendre pourquoi un test échoue, surtout si le rapport est volumineux, vous pouvez déplacer la comparaison dans la fonction expect. Par exemple, utilisez la méthode equals de la classe Buffer pour vérifier si les tampons contiennent le même contenu :

  • remplacer expect(received).toEqual(expected) par expect(received.equals(expected)).toBe(true)

  • remplacer expect(received).not.toEqual(expected) par expect(received.equals(expected)).toBe(false)

.toMatch(regexp | string)

Utilisez .toMatch pour vérifier qu'une chaîne correspond à une expression régulière.

Par exemple, vous pourriez ne pas savoir exactement ce que retourne essayOnTheBestFlavor(), mais vous savez que c'est une très longue chaîne et que la sous-chaîne grapefruit devrait s'y trouver quelque part. Vous pouvez tester cela avec :

describe('an essay on the best flavor', () => {
test('mentions grapefruit', () => {
expect(essayOnTheBestFlavor()).toMatch(/grapefruit/);
expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit'));
});
});

Ce matcher accepte également une chaîne de caractères, qu'il tentera de faire correspondre :

describe('grapefruits are healthy', () => {
test('grapefruits are a fruit', () => {
expect('grapefruits').toMatch('fruit');
});
});

.toMatchObject(object)

Utilisez .toMatchObject pour vérifier qu'un objet JavaScript correspond à un sous-ensemble des propriétés d'un autre objet. Il fera correspondre les objets reçus avec des propriétés absentes de l'objet attendu.

Vous pouvez aussi passer un tableau d'objets, auquel cas la méthode retournera vrai uniquement si chaque objet du tableau reçu correspond (au sens de toMatchObject décrit ci-dessus) à l'objet correspondant dans le tableau attendu. C'est utile si vous voulez vérifier que deux tableaux correspondent par leur nombre d'éléments, contrairement à arrayContaining qui autorise des éléments supplémentaires dans le tableau reçu.

Vous pouvez faire correspondre des propriétés à des valeurs ou à d'autres matchers.

const houseForSale = {
bath: true,
bedrooms: 4,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
area: 20,
wallColor: 'white',
},
};
const desiredHouse = {
bath: true,
kitchen: {
amenities: ['oven', 'stove', 'washer'],
wallColor: expect.stringMatching(/white|yellow/),
},
};

test('the house has my desired features', () => {
expect(houseForSale).toMatchObject(desiredHouse);
});
describe('toMatchObject applied to arrays', () => {
test('the number of elements must match exactly', () => {
expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]);
});

test('.toMatchObject is called for each elements, so extra object properties are okay', () => {
expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([
{foo: 'bar'},
{baz: 1},
]);
});
});

.toMatchSnapshot(propertyMatchers?, hint?)

Cette méthode vérifie qu'une valeur correspond au dernier instantané. Consultez le guide des tests par instantané pour plus d'informations.

Vous pouvez fournir un argument optionnel propertyMatchers sous forme d'objet, contenant des matchers asymétriques comme valeurs d'un sous-ensemble de propriétés attendues, uniquement si la valeur reçue est une instance d'objet. Cela équivaut à utiliser toMatchObject avec des critères flexibles pour un sous-ensemble de propriétés, suivi d'un test snapshot avec des critères exacts pour les propriétés restantes.

Vous pouvez fournir un argument optionnel hint (indice) de type chaîne qui est ajouté au nom du test. Bien que Jest ajoute toujours un numéro à la fin d'un nom de snapshot, des indices descriptifs courts peuvent être plus utiles que des numéros pour différencier plusieurs snapshots dans un seul bloc it ou test. Jest trie les snapshots par nom dans le fichier .snap correspondant.

.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)

Vérifie qu'une valeur correspond au dernier instantané.

Vous pouvez fournir un argument optionnel propertyMatchers sous forme d'objet, contenant des matchers asymétriques comme valeurs d'un sous-ensemble de propriétés attendues, uniquement si la valeur reçue est une instance d'objet. Cela équivaut à utiliser toMatchObject avec des critères flexibles pour un sous-ensemble de propriétés, suivi d'un test snapshot avec des critères exacts pour les propriétés restantes.

Jest ajoute l'argument chaîne inlineSnapshot au matcher dans le fichier de test (au lieu d'un fichier .snap externe) lors de la première exécution du test.

Consultez la section sur les Instantannés Inline pour plus d'informations.

.toStrictEqual(value)

Utilisez .toStrictEqual pour vérifier que des objets ont la même structure et le même type.

Différences par rapport à .toEqual :

  • les clés avec propriétés undefined sont vérifiées, ex. {a: undefined, b: 2} ne sera pas égal à {b: 2} ;

  • les éléments undefined sont pris en compte, ex. [2] ne sera pas égal à [2, undefined] ;

  • la parcimonie des tableaux est vérifiée, ex. [, 1] ne sera pas égal à [undefined, 1] ;

  • les types d'objets sont vérifiés, ex. une instance de classe avec champs a et b ne sera pas égale à un objet littéral avec champs a et b.

class LaCroix {
constructor(flavor) {
this.flavor = flavor;
}
}

describe('the La Croix cans on my desk', () => {
test('are not semantically the same', () => {
expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'});
expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'});
});
});

.toThrow(error?)

Utilisez .toThrow pour tester qu'une fonction lance une erreur lorsqu'elle est appelée. Par exemple, pour vérifier que drinkFlavor('octopus') lance une erreur car le goût poulpe est trop désagréable, nous pourrions écrire :

test('throws on octopus', () => {
expect(() => {
drinkFlavor('octopus');
}).toThrow();
});
astuce

Vous devez encapsuler le code dans une fonction, sinon l'erreur ne sera pas interceptée et l'assertion échouera.

Vous pouvez fournir un argument optionnel pour tester qu'une erreur spécifique est lancée :

  • expression régulière : le message d'erreur correspond au motif

  • chaîne de caractères : le message d'erreur contient la sous-chaîne

  • objet d'erreur : le message d'erreur est identique à la propriété message de l'objet

  • classe d'erreur : l'objet erreur est une instance de la classe

Par exemple, supposons que drinkFlavor soit codée ainsi :

function drinkFlavor(flavor) {
if (flavor === 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}

Nous pourrions tester que cette erreur est lancée de plusieurs manières :

test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}

// Test that the error message says "yuck" somewhere: these are equivalent
expect(drinkOctopus).toThrow(/yuck/);
expect(drinkOctopus).toThrow('yuck');

// Test the exact error message
expect(drinkOctopus).toThrow(/^yuck, octopus flavor$/);
expect(drinkOctopus).toThrow(new Error('yuck, octopus flavor'));

// Test that we get a DisgustingFlavorError
expect(drinkOctopus).toThrow(DisgustingFlavorError);
});

.toThrowErrorMatchingSnapshot(hint?)

Utilisez .toThrowErrorMatchingSnapshot pour vérifier qu'une fonction lance une erreur correspondant au snapshot le plus récent lorsqu'elle est appelée.

Vous pouvez fournir un argument optionnel hint (indice) de type chaîne qui est ajouté au nom du test. Bien que Jest ajoute toujours un numéro à la fin d'un nom de snapshot, des indices descriptifs courts peuvent être plus utiles que des numéros pour différencier plusieurs snapshots dans un seul bloc it ou test. Jest trie les snapshots par nom dans le fichier .snap correspondant.

Par exemple, supposons que vous ayez une fonction drinkFlavor qui lance une erreur lorsque le goût est 'octopus', codée ainsi :

function drinkFlavor(flavor) {
if (flavor === 'octopus') {
throw new DisgustingFlavorError('yuck, octopus flavor');
}
// Do some other stuff
}

Le test pour cette fonction ressemblera à ceci :

test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}

expect(drinkOctopus).toThrowErrorMatchingSnapshot();
});

Et il générera le snapshot suivant :

exports[`drinking flavors throws on octopus 1`] = `"yuck, octopus flavor"`;

Consultez le billet Testing React Trees with Snapshot pour plus d'informations sur les tests snapshot.

.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)

Utilisez .toThrowErrorMatchingInlineSnapshot pour vérifier qu'une fonction lance une erreur correspondant au snapshot le plus récent lorsqu'elle est appelée.

Jest ajoute l'argument chaîne inlineSnapshot au matcher dans le fichier de test (au lieu d'un fichier .snap externe) lors de la première exécution du test.

Consultez la section sur les Instantannés Inline pour plus d'informations.

Matchers asymétriques

expect.anything()

expect.anything() correspond à tout sauf null ou undefined. Utilisez-le dans toEqual ou toHaveBeenCalledWith à la place d'une valeur littérale. Par exemple, pour vérifier qu'une fonction mock est appelée avec un argument non nul :

test('map calls its argument with a non-null argument', () => {
const mock = jest.fn();
[1].map(x => mock(x));
expect(mock).toHaveBeenCalledWith(expect.anything());
});

expect.any(constructor)

expect.any(constructor) correspond à tout objet créé avec le constructeur donné ou aux primitives du type passé. Utilisez-le dans toEqual ou toHaveBeenCalledWith à la place d'une valeur littérale. Par exemple, pour vérifier qu'une fonction mock est appelée avec un nombre :

class Cat {}
function getCat(fn) {
return fn(new Cat());
}

test('randocall calls its callback with a class instance', () => {
const mock = jest.fn();
getCat(mock);
expect(mock).toHaveBeenCalledWith(expect.any(Cat));
});

function randocall(fn) {
return fn(Math.floor(Math.random() * 6 + 1));
}

test('randocall calls its callback with a number', () => {
const mock = jest.fn();
randocall(mock);
expect(mock).toHaveBeenCalledWith(expect.any(Number));
});

expect.arrayContaining(array)

expect.arrayContaining(array) correspond à un tableau reçu contenant tous les éléments du tableau attendu. Le tableau attendu est un sous-ensemble du tableau reçu, qui peut contenir des éléments supplémentaires.

Vous pouvez l'utiliser à la place d'une valeur littérale :

  • dans toEqual ou toHaveBeenCalledWith

  • pour correspondre à une propriété dans objectContaining ou toMatchObject

describe('arrayContaining', () => {
const expected = ['Alice', 'Bob'];
it('matches even if received contains additional elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
});
it('does not match if received does not contain expected elements', () => {
expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected));
});
});
describe('Beware of a misunderstanding! A sequence of dice rolls', () => {
const expected = [1, 2, 3, 4, 5, 6];
it('matches even with an unexpected number 7', () => {
expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual(
expect.arrayContaining(expected),
);
});
it('does not match without an expected number 2', () => {
expect([4, 1, 6, 7, 3, 5, 7, 5, 4, 6]).not.toEqual(
expect.arrayContaining(expected),
);
});
});

expect.not.arrayContaining(array)

expect.not.arrayContaining(array) correspond à un tableau reçu qui ne contient pas tous les éléments du tableau attendu. Le tableau attendu n'est pas un sous-ensemble du tableau reçu.

C'est l'inverse de expect.arrayContaining.

describe('not.arrayContaining', () => {
const expected = ['Samantha'];

it('matches if the actual array does not contain the expected elements', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(
expect.not.arrayContaining(expected),
);
});
});

expect.arrayOf(value)

expect.arrayOf(value) correspond à un tableau reçu dont tous les éléments correspondent au matcher fourni. Utile pour vérifier que chaque élément satisfait une condition ou un type spécifique.

Exemple :

test('all elements in array are strings', () => {
expect(['apple', 'banana', 'cherry']).toEqual(
expect.arrayOf(expect.any(String)),
);
});

Particulièrement utile pour valider des tableaux contenant des structures complexes :

test('array of objects with specific properties', () => {
expect([
{id: 1, name: 'Alice'},
{id: 2, name: 'Bob'},
]).toEqual(
expect.arrayOf(
expect.objectContaining({
id: expect.any(Number),
name: expect.any(String),
}),
),
);
});

expect.not.arrayOf(value)

expect.not.arrayOf(value) correspond à un tableau reçu dont certains éléments ne correspondent pas au matcher fourni.

Exemple :

test('not all elements in array are strings', () => {
expect(['apple', 123, 'cherry']).toEqual(
expect.not.arrayOf(expect.any(String)),
);
});

expect.closeTo(number, numDigits?)

expect.closeTo(number, numDigits?) est utile pour comparer des nombres à virgule flottante dans des propriétés d'objet ou des tableaux. Pour comparer directement un nombre, utilisez plutôt .toBeCloseTo.

L'argument optionnel numDigits limite le nombre de chiffres à vérifier après la virgule. Pour la valeur par défaut 2, le critère est Math.abs(expected - received) < 0.005 (that is, 10 ** -2 / 2).

Exemple avec une précision de 5 chiffres :

test('compare float in object properties', () => {
expect({
title: '0.1 + 0.2',
sum: 0.1 + 0.2,
}).toEqual({
title: '0.1 + 0.2',
sum: expect.closeTo(0.3, 5),
});
});

expect.objectContaining(object)

expect.objectContaining(object) correspond à tout objet reçu contenant récursivement les propriétés attendues. L'objet attendu est un sous-ensemble de l'objet reçu, qui peut contenir des propriétés supplémentaires.

Les valeurs de l'objet attendu peuvent être des matchers comme expect.anything().

Exemple : vérifier qu'un objet Event passé à onPress contient les propriétés event.x et event.y :

test('onPress gets called with the right thing', () => {
const onPress = jest.fn();
simulatePresses(onPress);
expect(onPress).toHaveBeenCalledWith(
expect.objectContaining({
x: expect.any(Number),
y: expect.any(Number),
}),
);
});

expect.not.objectContaining(object)

expect.not.objectContaining(object) correspond à tout objet reçu ne contenant pas récursivement les propriétés attendues. L'objet attendu n'est pas un sous-ensemble de l'objet reçu.

C'est l'inverse de expect.objectContaining.

describe('not.objectContaining', () => {
const expected = {foo: 'bar'};

it('matches if the actual object does not contain expected key: value pairs', () => {
expect({bar: 'baz'}).toEqual(expect.not.objectContaining(expected));
});
});

expect.stringContaining(string)

expect.stringContaining(string) valide la valeur reçue si c'est une chaîne contenant exactement la chaîne attendue.

expect.not.stringContaining(string)

expect.not.stringContaining(string) valide la valeur reçue si ce n'est pas une chaîne ou si c'est une chaîne ne contenant pas exactement la chaîne attendue.

C'est l'inverse de expect.stringContaining.

describe('not.stringContaining', () => {
const expected = 'Hello world!';

it('matches if the received value does not contain the expected substring', () => {
expect('How are you?').toEqual(expect.not.stringContaining(expected));
});
});

expect.stringMatching(string | regexp)

expect.stringMatching(string | regexp) valide la valeur reçue si c'est une chaîne correspondant à la chaîne ou expression régulière attendue.

Vous pouvez l'utiliser à la place d'une valeur littérale :

  • dans toEqual ou toHaveBeenCalledWith

  • pour correspondre à un élément dans arrayContaining

  • pour correspondre à une propriété dans objectContaining ou toMatchObject

Cet exemple montre également comment imbriquer plusieurs matchers asymétriques, avec expect.stringMatching à l'intérieur de expect.arrayContaining.

describe('stringMatching in arrayContaining', () => {
const expected = [
expect.stringMatching(/^Alic/),
expect.stringMatching(/^[BR]ob/),
];
it('matches even if received contains additional elements', () => {
expect(['Alicia', 'Roberto', 'Evelina']).toEqual(
expect.arrayContaining(expected),
);
});
it('does not match if received does not contain expected elements', () => {
expect(['Roberto', 'Evelina']).not.toEqual(
expect.arrayContaining(expected),
);
});
});

expect.not.stringMatching(string | regexp)

expect.not.stringMatching(string | regexp) valide la valeur reçue si ce n'est pas une chaîne ou si c'est une chaîne ne correspondant pas à la chaîne ou expression régulière attendue.

C'est l'inverse de expect.stringMatching.

describe('not.stringMatching', () => {
const expected = /Hello world!/;

it('matches if the received value does not match the expected regex', () => {
expect('How are you?').toEqual(expect.not.stringMatching(expected));
});
});

Vérification du nombre d'assertions

expect.assertions(number)

expect.assertions(number) vérifie qu'un certain nombre d'assertions sont appelées pendant un test. Utile pour tester du code asynchrone, afin de s'assurer que les assertions dans un callback sont bien exécutées.

Par exemple, supposons une fonction doAsync recevant deux callbacks callback1 et callback2 qu'elle appelle de manière asynchrone dans un ordre inconnu. On peut tester cela avec :

test('doAsync calls both callbacks', () => {
expect.assertions(2);
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}

doAsync(callback1, callback2);
});

L'appel expect.assertions(2) garantit que les deux callbacks sont bien appelés.

expect.hasAssertions()

expect.hasAssertions() vérifie qu'au moins une assertion est appelée pendant un test. Utile pour tester du code asynchrone, afin de s'assurer que les assertions dans un callback sont bien exécutées.

Par exemple, supposons plusieurs fonctions gérant un état. prepareState appelle un callback avec un objet d'état, validateState traite cet objet, et waitOnState retourne une promesse attendant que tous les callbacks de prepareState se terminent. On peut tester cela avec :

test('prepareState prepares a valid state', () => {
expect.hasAssertions();
prepareState(state => {
expect(validateState(state)).toBeTruthy();
});
return waitOnState();
});

L'appel expect.hasAssertions() garantit que le callback de prepareState est bien appelé.

Utilitaires d'extension

expect.addEqualityTesters(testers)

Utilisez expect.addEqualityTesters pour ajouter vos propres méthodes testant l'égalité entre deux objets. Par exemple, si vous avez une classe représentant un volume pouvant déterminer si deux volumes avec unités différentes sont égaux. Vous pouvez souhaiter que toEqual (et d'autres matchers d'égalité) utilise cette méthode personnalisée lors de la comparaison d'instances de la classe Volume. Vous pouvez ajouter un testeur d'égalité personnalisé pour que toEqual détecte et applique une logique personnalisée lors de la comparaison d'instances de Volume :

Volume.js
// For simplicity in this example, we'll just support the units 'L' and 'mL'
export class Volume {
constructor(amount, unit) {
this.amount = amount;
this.unit = unit;
}

toString() {
return `[Volume ${this.amount}${this.unit}]`;
}

equals(other) {
if (this.unit === other.unit) {
return this.amount === other.amount;
} else if (this.unit === 'L' && other.unit === 'mL') {
return this.amount * 1000 === other.unit;
} else {
return this.amount === other.unit * 1000;
}
}
}
areVolumesEqual.js
import {expect} from '@jest/globals';
import {Volume} from './Volume.js';

function areVolumesEqual(a, b) {
const isAVolume = a instanceof Volume;
const isBVolume = b instanceof Volume;

if (isAVolume && isBVolume) {
return a.equals(b);
} else if (isAVolume === isBVolume) {
return undefined;
} else {
return false;
}
}

expect.addEqualityTesters([areVolumesEqual]);
__tests__/Volume.test.js
import {expect, test} from '@jest/globals';
import {Volume} from '../Volume.js';
import '../areVolumesEqual.js';

test('are equal with different units', () => {
expect(new Volume(1, 'L')).toEqual(new Volume(1000, 'mL'));
});

API des testeurs d'égalité personnalisés

Les testeurs personnalisés sont des fonctions retournant soit le résultat (true ou false) de la comparaison des deux arguments, soit undefined si le testeur ne gère pas ces objets et délègue la comparaison à d'autres testeurs.

Les testeurs personnalisés reçoivent 3 arguments : les deux objets à comparer et le tableau des testeurs personnalisés (utilisé pour les testeurs récursifs).

Ces fonctions auxiliaires et propriétés sont disponibles via this dans un testeur personnalisé :

this.equals(a, b, customTesters?)

Cette fonction d'égalité en profondeur renvoie true si deux objets ont des valeurs identiques (récursivement). Elle accepte optionnellement une liste de testeurs d'égalité personnalisés à appliquer aux vérifications. Si vous utilisez cette fonction, transmettez les testeurs personnalisés reçus afin que les vérifications d'égalité ultérieures qu'equals applique puissent également utiliser les testeurs configurés par l'auteur du test. Voir l'exemple dans la section Testeurs d'égalité personnalisés récursifs pour plus de détails.

Matchers vs Testeurs

Les matchers sont des méthodes disponibles sur expect, comme expect().toEqual(). toEqual est un matcher. Un testeur est une méthode utilisée par les matchers pour déterminer si des objets sont identiques via des vérifications d'égalité.

Les matchers personnalisés sont utiles pour fournir des assertions spécifiques que les auteurs de tests peuvent utiliser. Par exemple, l'exemple toBeWithinRange dans la section expect.extend illustre bien un matcher personnalisé. Parfois un auteur voudra vérifier l'égalité exacte de nombres avec toBe, mais d'autres fois toBeWithinRange sera plus adapté pour autoriser une certaine flexibilité.

Les testeurs d'égalité personnalisés sont idéaux pour étendre globalement les matchers de Jest avec une logique d'égalité personnalisée. Contrairement aux matchers, les auteurs ne peuvent pas activer/désactiver les testeurs pour des assertions spécifiques (utilisez plutôt un matcher personnalisé si nécessaire). Par exemple, définir comment comparer deux objets Volume pour tous les matchers relève d'un testeur d'égalité personnalisé.

Testeurs d'égalité personnalisés récursifs

Si vos testeurs personnalisés comparent des objets contenant des propriétés nécessitant une égalité en profondeur, utilisez l'utilitaire this.equals. Cette méthode equals est la même que celle utilisée en interne par Jest pour les comparaisons complexes. Elle appelle votre testeur personnalisé et accepte un troisième argument : un tableau de testeurs personnalisés. Transmettez cet argument lors des appels récursifs à equals pour que les vérifications profondes bénéficient également des testeurs personnalisés.

Par exemple, supposons que vous ayez une classe Book contenant un tableau de classes Author, et que ces deux classes disposent de testeurs personnalisés. Le testeur personnalisé de Book effectuerait une vérification d'égalité en profondeur sur le tableau de Author en transmettant les testeurs reçus, afin que le testeur d'égalité personnalisé de Author soit appliqué :

customEqualityTesters.js
function areAuthorEqual(a, b) {
const isAAuthor = a instanceof Author;
const isBAuthor = b instanceof Author;

if (isAAuthor && isBAuthor) {
// Authors are equal if they have the same name
return a.name === b.name;
} else if (isAAuthor === isBAuthor) {
return undefined;
} else {
return false;
}
}

function areBooksEqual(a, b, customTesters) {
const isABook = a instanceof Book;
const isBBook = b instanceof Book;

if (isABook && isBBook) {
// Books are the same if they have the same name and author array. We need
// to pass customTesters to equals here so the Author custom tester will be
// used when comparing Authors
return (
a.name === b.name && this.equals(a.authors, b.authors, customTesters)
);
} else if (isABook === isBBook) {
return undefined;
} else {
return false;
}
}

expect.addEqualityTesters([areAuthorsEqual, areBooksEqual]);
note

Définissez vos testeurs d'égalité comme des fonctions classiques (pas des fonctions fléchées) pour accéder aux utilitaires du contexte comme this.equals.

expect.addSnapshotSerializer(serializer)

Appelez expect.addSnapshotSerializer pour ajouter un module qui formate des structures de données spécifiques à votre application.

Dans un fichier de test individuel, un module ajouté est prioritaire sur ceux de la configuration snapshotSerializers, elle-même prioritaire sur les sérialiseurs par défaut pour les types JavaScript natifs et les éléments React. Le dernier module ajouté est le premier testé.

import serializer from 'my-serializer-module';
expect.addSnapshotSerializer(serializer);

// affects expect(value).toMatchSnapshot() assertions in the test file

L'ajout de sérialiseurs dans des fichiers de test individuels plutôt que dans la configuration snapshotSerializers :

  • Rend les dépendances explicites plutôt qu'implicites

  • Évite les limitations de configuration pouvant nécessiter l'ejection de create-react-app

Voir la configuration de Jest pour plus d'informations.

expect.extend(matchers)

Utilisez expect.extend pour ajouter vos propres matchers à Jest. Par exemple, si vous testez une bibliothèque de manipulation numérique et vérifiez souvent qu'un nombre se situe dans une plage spécifique, vous pourriez créer un matcher toBeWithinRange :

toBeWithinRange.js
import {expect} from '@jest/globals';

function toBeWithinRange(actual, floor, ceiling) {
if (
typeof actual !== 'number' ||
typeof floor !== 'number' ||
typeof ceiling !== 'number'
) {
throw new TypeError('These must be of type number!');
}

const pass = actual >= floor && actual <= ceiling;
if (pass) {
return {
message: () =>
`expected ${this.utils.printReceived(
actual,
)} not to be within range ${this.utils.printExpected(
`${floor} - ${ceiling}`,
)}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${this.utils.printReceived(
actual,
)} to be within range ${this.utils.printExpected(
`${floor} - ${ceiling}`,
)}`,
pass: false,
};
}
}

expect.extend({
toBeWithinRange,
});
__tests__/ranges.test.js
import {expect, test} from '@jest/globals';
import '../toBeWithinRange';

test('is within range', () => expect(100).toBeWithinRange(90, 110));

test('is NOT within range', () => expect(101).not.toBeWithinRange(0, 100));

test('asymmetric ranges', () => {
expect({apples: 6, bananas: 3}).toEqual({
apples: expect.toBeWithinRange(1, 10),
bananas: expect.not.toBeWithinRange(11, 20),
});
});
toBeWithinRange.d.ts
// optionally add a type declaration, e.g. it enables autocompletion in IDEs
declare module 'expect' {
interface AsymmetricMatchers {
toBeWithinRange(floor: number, ceiling: number): void;
}
interface Matchers<R> {
toBeWithinRange(floor: number, ceiling: number): R;
}
}

export {};
astuce

La déclaration de type du matcher peut résider dans un fichier .d.ts ou dans un module .ts importé (voir les exemples JS et TS ci-dessus respectivement). Si vous conservez la déclaration dans un fichier .d.ts, assurez-vous qu'il est inclus dans le programme et qu'il s'agit d'un module valide, c'est-à-dire qu'il contient au moins un export {} vide.

astuce

Au lieu d'importer le module toBeWithinRange dans le fichier de test, vous pouvez activer le matcher pour tous les tests en déplaçant l'appel expect.extend dans un script setupFilesAfterEnv :

import {expect} from '@jest/globals';
// remember to export `toBeWithinRange` as well
import {toBeWithinRange} from './toBeWithinRange';

expect.extend({
toBeWithinRange,
});

Matchers asynchrones

expect.extend prend également en charge les matchers asynchrones. Les matchers asynchrones renvoient une Promise, vous devrez donc attendre la valeur retournée. Utilisons un exemple de matcher pour illustrer leur utilisation. Nous allons implémenter un matcher appelé toBeDivisibleByExternalValue, où le nombre divisible sera récupéré depuis une source externe.

expect.extend({
async toBeDivisibleByExternalValue(received) {
const externalValue = await getExternalValueFromRemoteSource();
const pass = received % externalValue === 0;
if (pass) {
return {
message: () =>
`expected ${received} not to be divisible by ${externalValue}`,
pass: true,
};
} else {
return {
message: () =>
`expected ${received} to be divisible by ${externalValue}`,
pass: false,
};
}
},
});

test('is divisible by external value', async () => {
await expect(100).toBeDivisibleByExternalValue();
await expect(101).not.toBeDivisibleByExternalValue();
});

API des matchers personnalisés

Les matchers doivent renvoyer un objet (ou une Promise d'un objet) avec deux clés. pass indique s'il y a eu correspondance ou non, et message fournit une fonction sans arguments qui renvoie un message d'erreur en cas d'échec. Ainsi, lorsque pass est faux, message doit renvoyer le message d'erreur lorsque expect(x).yourMatcher() échoue. Et lorsque pass est vrai, message doit renvoyer le message d'erreur lorsque expect(x).not.yourMatcher() échoue.

Les matchers sont appelés avec l'argument passé à expect(x) suivi des arguments passés à .yourMatcher(y, z) :

expect.extend({
yourMatcher(x, y, z) {
return {
pass: true,
message: () => '',
};
},
});

Ces fonctions d'aide et propriétés sont disponibles sur this à l'intérieur d'un matcher personnalisé :

this.isNot

Un booléen vous indiquant si ce matcher a été appelé avec le modificateur de négation .not, vous permettant d'afficher un indice de matcher clair et correct (voir l'exemple de code).

this.promise

Une chaîne de caractères vous permettant d'afficher un indice de matcher clair et correct :

  • 'rejects' si le matcher a été appelé avec le modificateur de promesse .rejects

  • 'resolves' si le matcher a été appelé avec le modificateur de promesse .resolves

  • '' si le matcher n'a pas été appelé avec un modificateur de promesse

this.equals(a, b, customTesters?)

Il s'agit d'une fonction d'égalité profonde qui renverra true si deux objets ont les mêmes valeurs (récursivement). Elle prend optionnellement une liste de testeurs d'égalité personnalisés à appliquer aux vérifications d'égalité profonde (voir this.customTesters ci-dessous).

this.expand

Un booléen vous indiquant si ce matcher a été appelé avec une option expand. Lorsque Jest est appelé avec le flag --expand, this.expand peut être utilisé pour déterminer si Jest doit afficher les différences et erreurs complètes.

this.utils

Plusieurs outils utiles sont exposés dans this.utils, comprenant principalement les exports de jest-matcher-utils.

Les plus utiles sont matcherHint, printExpected et printReceived pour formater joliment les messages d'erreur. Par exemple, regardez l'implémentation du matcher toBe :

const {diff} = require('jest-diff');
expect.extend({
toBe(received, expected) {
const options = {
comment: 'Object.is equality',
isNot: this.isNot,
promise: this.promise,
};

const pass = Object.is(received, expected);

const message = pass
? () =>
// eslint-disable-next-line prefer-template
this.utils.matcherHint('toBe', undefined, undefined, options) +
'\n\n' +
`Expected: not ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`
: () => {
const diffString = diff(expected, received, {
expand: this.expand,
});
return (
// eslint-disable-next-line prefer-template
this.utils.matcherHint('toBe', undefined, undefined, options) +
'\n\n' +
(diffString && diffString.includes('- Expect')
? `Difference:\n\n${diffString}`
: `Expected: ${this.utils.printExpected(expected)}\n` +
`Received: ${this.utils.printReceived(received)}`)
);
};

return {actual: received, message, pass};
},
});

Cela affichera quelque chose comme ceci :

  expect(received).toBe(expected)

Expected value to be (using Object.is):
"banana"
Received:
"apple"

Lorsqu'une assertion échoue, le message d'erreur doit donner suffisamment d'indications à l'utilisateur pour qu'il puisse résoudre son problème rapidement. Vous devez formuler un message d'échec précis pour garantir une bonne expérience développeur aux utilisateurs de vos assertions personnalisées.

this.customTesters

Si votre matcher effectue une vérification d'égalité profonde avec this.equals, vous pouvez transmettre des testeurs personnalisés fournis par l'utilisateur à this.equals. Les testeurs d'égalité personnalisés ajoutés via l'API addEqualityTesters sont disponibles via cette propriété. Les matchers intégrés de Jest passent this.customTesters (avec d'autres testeurs internes) à this.equals pour l'égalité profonde, et vos matchers personnalisés peuvent suivre la même approche.

Matchers personnalisés de snapshots

Pour utiliser les snapshots dans un matcher personnalisé, importez jest-snapshot et utilisez-le dans votre matcher.

Voici un matcher de snapshot qui tronque une chaîne à une longueur donnée, .toMatchTrimmedSnapshot(length) :

const {toMatchSnapshot} = require('jest-snapshot');

expect.extend({
toMatchTrimmedSnapshot(received, length) {
return toMatchSnapshot.call(
this,
received.slice(0, length),
'toMatchTrimmedSnapshot',
);
},
});

it('stores only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedSnapshot(10);
});

/*
Stored snapshot will look like:

exports[`stores only 10 characters: toMatchTrimmedSnapshot 1`] = `"extra long"`;
*/

Il est possible de créer des matchers personnalisés pour les snapshots inline, les snapshots seront correctement ajoutés aux matchers. Cependant, les snapshots inline tentent toujours de s'ajouter au premier argument ou au second quand le premier est un matcher de propriété, ce qui empêche d'accepter des arguments personnalisés dans les matchers.

const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
toMatchTrimmedInlineSnapshot(received, ...rest) {
return toMatchInlineSnapshot.call(this, received.slice(0, 10), ...rest);
},
});

it('stores only 10 characters', () => {
expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
expect('extra long string oh my gerd').toMatchTrimmedInlineSnapshot(
`"extra long"`
);
*/
});

asynchrone

Si votre matcher de snapshot inline est asynchrone (utilisant async-await), vous pourriez rencontrer l'erreur "Multiple inline snapshots for the same call are not supported". Jest nécessite des informations contextuelles supplémentaires pour localiser l'utilisation du matcher et mettre à jour correctement les snapshots.

const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
async toMatchObservationInlineSnapshot(fn, ...rest) {
// The error (and its stacktrace) must be created before any `await`
this.error = new Error();

// The implementation of `observe` doesn't matter.
// It only matters that the custom snapshot matcher is async.
const observation = await observe(async () => {
await fn();
});

return toMatchInlineSnapshot.call(this, recording, ...rest);
},
});

it('observes something', async () => {
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot();
/*
The snapshot will be added inline like
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot(`"async action"`);
*/
});

Sortie anticipée

Normalement, jest tente de faire correspondre tous les snapshots attendus dans un test.

Il peut être inutile de poursuivre le test si un snapshot précédent échoue. Par exemple, lorsque vous capturez des snapshots d'une machine à états après différentes transitions, vous pouvez interrompre le test dès qu'une transition produit un état incorrect.

Dans ce cas, implémentez un matcher de snapshot personnalisé qui déclenche une erreur dès la première non-correspondance plutôt que de collecter tous les écarts.

const {toMatchInlineSnapshot} = require('jest-snapshot');

expect.extend({
toMatchStateInlineSnapshot(...args) {
this.dontThrow = () => {};

return toMatchInlineSnapshot.call(this, ...args);
},
});

let state = 'initial';

function transition() {
// Typo in the implementation should cause the test to fail
if (state === 'INITIAL') {
state = 'pending';
} else if (state === 'pending') {
state = 'done';
}
}

it('transitions as expected', () => {
expect(state).toMatchStateInlineSnapshot(`"initial"`);

transition();
// Already produces a mismatch. No point in continuing the test.
expect(state).toMatchStateInlineSnapshot(`"loading"`);

transition();
expect(state).toMatchStateInlineSnapshot(`"done"`);
});

Propriétés sérialisables

SERIALIZABLE_PROPERTIES

Les propriétés sérialisables forment un ensemble de propriétés considérées comme sérialisables par Jest. Cet ensemble détermine si une propriété doit être sérialisée ou non. Si un objet possède une propriété absente de cet ensemble, elle est considérée comme non sérialisable et n'apparaîtra pas dans les messages d'erreur.

Vous pouvez ajouter vos propres propriétés à cet ensemble pour garantir l'affichage correct de vos objets. Par exemple, avec une classe Volume, si vous souhaitez afficher uniquement les propriétés amount et unit, ajoutez-les à SERIALIZABLE_PROPERTIES :

import {SERIALIZABLE_PROPERTIES} from 'jest-matcher-utils';

class Volume {
constructor(amount, unit) {
this.amount = amount;
this.unit = unit;
}

get label() {
throw new Error('Not implemented');
}
}

Volume.prototype[SERIALIZABLE_PROPERTIES] = ['amount', 'unit'];

expect(new Volume(1, 'L')).toEqual(new Volume(10, 'L'));

Cela affichera uniquement amount et unit dans les messages d'erreur, en ignorant la propriété label.

expect(received).toEqual(expected) // deep equality

Expected: {"amount": 10, "unit": "L"}
Received: {"amount": 1, "unit": "L"}