Expect
Lorsque vous écrivez des tests, vous devez souvent vérifier que les valeurs remplissent certaines conditions. expect
vous donne accès à un certain nombre de « comparateurs » qui vous permettent de valider différentes choses.
For additional Jest matchers maintained by the Jest Community check out jest-extended
.
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';
Veuillez consulter le guide Premiers pas pour plus de détails sur la façon de configurer Jest avec TypeScript.
Référence
- Expect
- Modifiers
- Matchers
.toBe(value)
.toHaveBeenCalled()
.toHaveBeenCalledTimes(number)
.toHaveBeenCalledWith(arg1, arg2, ...)
.toHaveBeenLastCalledWith(arg1, arg2, ...)
.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)
.toHaveReturned()
.toHaveReturnedTimes(number)
.toHaveReturnedWith(value)
.toHaveLastReturnedWith(value)
.toHaveNthReturnedWith(nthCall, value)
.toHaveLength(number)
.toHaveProperty(keyPath, value?)
.toBeCloseTo(number, numDigits?)
.toBeDefined()
.toBeFalsy()
.toBeGreaterThan(number | bigint)
.toBeGreaterThanOrEqual(number | bigint)
.toBeLessThan(number | bigint)
.toBeLessThanOrEqual(number | bigint)
.toBeInstanceOf(Class)
.toBeNull()
.toBeTruthy()
.toBeUndefined()
.toBeNaN()
.toContain(item)
.toContainEqual(item)
.toEqual(value)
.toMatch(regexp | string)
.toMatchObject(object)
.toMatchSnapshot(propertyMatchers?, hint?)
.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)
.toStrictEqual(value)
.toThrow(error?)
.toThrowErrorMatchingSnapshot(hint?)
.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
- Asymmetric Matchers
expect.anything()
expect.any(constructor)
expect.arrayContaining(array)
expect.not.arrayContaining(array)
expect.closeTo(number, numDigits?)
expect.objectContaining(object)
expect.not.objectContaining(object)
expect.stringContaining(string)
expect.not.stringContaining(string)
expect.stringMatching(string | regexp)
expect.not.stringMatching(string | regexp)
- Assertion Count
- Extend Utilities
Expect
expect(valeur)
La fonction expect
est utilisée à chaque fois que vous souhaitez tester une valeur. Vous appellerez rarement expect
par lui-même. Au lieu de cela, vous utiliserez expect
avec une fonction « comparateur » pour vérifier quelque chose sur une valeur.
Il est plus facile de comprendre cela avec un exemple. Supposons que vous ayez une méthode bestLaCroixFlavor()
qui est censée renvoyer la chaîne 'grapefruit'
. Voici comment vous pourriez tester cela :
test('la meilleure saveur est le pamplemousse', () => {
expect(bestLaCroixFlavor()).toBe('grapefruit');
});
Dans ce cas, toBe
est la fonction comparateur. Il existe un grand nombre de fonctions comparateur différentes, documentées ci-dessous, pour vous aider à tester différentes choses.
L'argument de expect
doit être la valeur que votre code produit, et le paramètre du comparateur doit être la valeur correcte. Si vous les confondez, vos tests fonctionneront toujours, mais les messages d'erreur des tests qui échouent seront étranges.
Modifiers
.not
Si vous savez comment tester quelque chose, .not
vous permet de tester son opposé. Par exemple, ce code teste que le meilleur goût de La Croix n'est pas coconut :
test('la meilleure saveur n\'est pas coconut', () => {
expect(bestLaCroixFlavor()).not.toBe('coconut');
});
.resolves
Utilisez resolves
pour déballer la valeur d'une promesse remplie afin que tout autre comparateur puisse être enchaîné. Si la promesse est rejetée, l'assertion échoue.
Par exemple, ce code teste que la promesse se résout et que la valeur résultante est 'lemon'
:
test('resolves to lemon', () => {
// assurez-vous d'ajouter une instruction return
return expect(Promise.resolve('lemon')).resolves.toBe('lemon');
});
Since you are still testing promises, the test is still asynchronous. Par conséquent, vous devrez dire à Jest d'attendre en retournant l'assertion non enveloppée.
Alternativement, vous pouvez utiliser async/wait
en combinaison 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 déballer la raison d'une promesse rejetée afin que tout autre comparateur puisse être enchaîné. Si la promesse est remplie, l'assertion échoue.
Par exemple, ce code teste que la promesse rejette avec la raison 'octopus'
:
test('rejects to octopus', () => {
// assurez-vous d'ajouter une instruction return
return expect(Promise.reject(new Error('octopus'))).rejects.toThrow(
'octopus',
);
});
Since you are still testing promises, the test is still asynchronous. Par conséquent, vous devrez dire à Jest d'attendre en retournant l'assertion non enveloppée.
Alternativement, vous pouvez utiliser async/wait
en combinaison 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 pour vérifier l'identité référentielle d'instances d'objets. Il appelle Object.is
pour comparer les valeurs, ce qui est encore mieux pour les tests que l'opérateur d'égalité stricte ===
.
Par exemple, ce code validera certaines propriétés de l'objet can
:
const can = {
name: 'pamplemousse',
ounces: 12,
};
describe('le can', () => {
test('a 12 ounces', () => {
expect(can.ounces).toBe(12);
});
test('a un nom sophistiqué', () => {
expect(can.name).toBe('pamplemousse');
});
});
N'utilisez pas .toBe
avec des nombres à virgule flottante. Par exemple, en raison de l'arrondi, en JavaScript 0.2 + 0.1
n'est pas strictement égal à 0.3
. Si vous avez des nombres à virgule flottante, essayez à la place .toBeCloseTo
.
Bien que le comparateur .toBe
vérifie l'identité référentielle, il reporte une comparaison profonde des valeurs si l'assertion échoue. 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 ont ou non la même instance :
- rewrite
expect(received).toBe(expected)
asexpect(Object.is(received, expected)).toBe(true)
- rewrite
expect(received).not.toBe(expected)
asexpect(Object.is(received, expected)).toBe(false)
.toHaveBeenCalled()
Aussi sous l'alias : .toBeCalled()
Use .toHaveBeenCalled
to ensure that a mock function was called.
Par exemple, supposons que vous avez une fonction drinkAll(boisson, flavour)
qui prend une fonction drink
et l'applique à toutes les boissons disponibles. You might want to check that drink
gets called. Vous pouvez le faire avec cette suite de tests :
function drinkAll(callback, flavour) {
if (flavour !== 'octopus') {
callback(flavour);
}
}
describe('drinkAll', () => {
test('boit quelque chose à la saveur lemon', () => {
const drink = jest.fn();
drinkAll(drink, 'lemon');
expect(drink).toHaveBeenCalled();
});
test('ne boit pas quelque chose à la saveur octopus', () => {
const drink = jest.fn();
drinkAll(drink, 'octopus');
expect(drink).not.toHaveBeenCalled();
});
});
.toHaveBeenCalledTimes(number)
Aussi sous l'alias : .toBeCalledTimes(number)
Utilisez .toHaveBeenCalledTimes
pour vous assurer qu'une fonction simulée a été appelée le nombre exact 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 passées. Vous pouvez vérifier que la fonction drink a été appelée le nombre exact de fois. Vous pouvez le faire avec cette suite de tests :
test('drinkEach boit chaque drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenCalledTimes(2);
});
.toHaveBeenCalledWith(arg1, arg2, ...)
Aussi sous l'alias : .toBeCalledWith()
Utilisez .toHaveBeenCalledWith
pour vous assurer qu'une fonction simulée a été appelée avec des arguments spécifiques. Les arguments sont vérifiés avec le même algorithme que celui de .toEqual
.
Par exemple, supposons que vous pouvez enregistrer une boisson avec une fonction register
, et que applyToAll(f)
devrait appliquer la fonction f
à toutes les boissons enregistrées. Pour vous assurer que cela fonctionne, vous pouvez écrire :
test('l\'enregistrement s\'applique correctement à orange La Croix', () => {
const beverage = new LaCroix('orange');
register(beverage);
const f = jest.fn();
applyToAll(f);
expect(f).toHaveBeenCalledWith(beverage);
});
.toHaveBeenLastCalledWith(arg1, arg2, ...)
Aussi sous l'alias : .lastCalledWith(arg1, arg2, ...)
Si vous avez une fonction simulée, vous pouvez utiliser .toHaveBeenLastCalledWith
pour tester avec quels arguments il a été appelé en dernier. Par exemple, supposons que vous avez une fonction applyToAllFlavors(f)
qui applique f
à un tas de saveurs, et que vous voulez vous assurer que lorsque vous l'appelez, la dernière saveur sur laquelle elle opère est 'mango'
. Vous pouvez écrire :
test('appliquation à tous les parfums, mango est le dernier', () => {
const drink = jest.fn();
applyToAllFlavors(drink);
expect(drink).toHaveBeenLastCalledWith('mango');
});
.toHaveBeenNthCalledWith(nthCall, arg1, arg2, ....)
Aussi sous l'alias : .nthCalledWith(nthCall, arg1, arg2, ...)
Si vous avez une fonction simulée, vous pouvez utiliser .toHaveBeenNthCalledWith
pour tester avec quels arguments elle a été énièmement appelée. Par exemple, supposons que vous ayez une fonction drinkEach(drink, Array<flavor>)
qui applique f
à un tas de saveurs, et vous voulez vous assurer que lorsque vous l'appelez, la première saveur sur laquelle elle opère est 'lemon'
et la seconde 'octopus'
. Vous pouvez écrire :
test('drinkEach boit chaque drink', () => {
const drink = jest.fn();
drinkEach(drink, ['lemon', 'octopus']);
expect(drink).toHaveBeenNthCalledWith(1, 'lemon');
expect(drink).toHaveBeenNthCalledWith(2, 'octopus');
});
L'argument nth doit être un entier positif à partir de 1.
.toHaveReturned()
Aussi sous l'alias : .toReturn()
Si vous avez une fonction simulée, vous pouvez utiliser .toHaveReturned
pour tester que la fonction simulée a retourné avec succès (c'est-à-dire qu'elle n'a pas lancé d'erreur) au moins une fois. Par exemple, supposons que vous avez un drink
simulé qui renvoie true
. Vous pouvez écrire :
test('drinks retourne', () => {
const drink = jest.fn(() => true);
drink();
expect(drink).toHaveReturned();
});
.toHaveReturnedTimes(number)
Aussi sous l'alias : .toReturnTimes(nombre)
Utilisez .toHaveReturnedTimes
pour vous assurer qu'une fonction simulée est retournée avec succès (c'est-à-dire qu'elle n'a pas lancé d'erreur) un nombre exact de fois. Les appels à la fonction simulée qui provoquent une erreur ne sont pas comptabilisés dans le nombre de retours de la fonction.
Par exemple, supposons que vous avez un drink
simulé qui renvoie true
. Vous pouvez écrire :
test('drink retourne 2 fois', () => {
const drink = jest.fn(() => true);
drink();
drink();
expect(drink).toHaveReturnedTimes(2);
});
.toHaveReturnedWith(value)
Aussi sous l'alias : .toReturnWith(value)
Utilisez .toHaveReturnedWith
pour vous assurer qu'une fonction simulée a retourné une valeur spécifique.
Par exemple, supposons que vous ayez un drink
simulé qui renvoie le nom de la boisson qui a été consommée. Vous pouvez écrire :
test('drink retourne La Croix', () => {
const beverage = {name: 'La Croix'};
const drink = jest.fn(beverage => beverage.name);
drink(beverage);
expect(drink).toHaveReturnedWith('La Croix');
});
.toHaveLastReturnedWith(value)
Aussi sous l'alias : .lastReturnedWith(value)
Utilisez .toHaveLastReturnedWith
pour tester la valeur spécifique que la dernière fonction simulée a retournée. Si le dernier appel à la fonction simulée a généré une erreur, alors ce comparateur échouera, quelle que soit la valeur que vous avez fournie comme valeur de retour attendue.
Par exemple, supposons que vous ayez un drink
simulé qui renvoie le nom de la boisson qui a été consommée. Vous pouvez écrire :
test('drink retourne La Croix (Orange) en dernier', () => {
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)
Aussi sous l'alias : .nthReturnedWith(nthCall, valeur)
Utilisez .toHaveNthReturnedWith
pour tester la valeur spécifique retournée par une fonction simulée pour le nième appel. Si le nième appel à la fonction simulée a lancé une erreur, alors ce résultat échouera quelle que soit la valeur que vous avez fournie comme valeur de retour attendue.
Par exemple, supposons que vous ayez un drink
simulé qui renvoie le nom de la boisson qui a été consommée. Vous pouvez écrire :
test('drink retourne les nièmes appels attendus', () => {
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)');
});
L'argument nth doit être un entier positif à partir de 1.
.toHaveLength(number)
Utilisez .toHaveLength
pour vérifier qu'un objet a une propriété .length
et il est défini à une certaine valeur numérique.
Ceci est particulièrement utile pour vérifier la taille des tableaux ou des chaînes.
expect([1, 2, 3]).toHaveLength(3);
expect('abc').toHaveLength(3);
expect('').not.toHaveLength(5);
.toHaveProperty(keyPath, value?)
Utilisez .toHaveProperty
pour vérifier si la propriété pour la référence fournie keyPath
existe pour un objet. Pour vérifier les propriétés profondément imbriquées dans un objet, vous pouvez utiliser la notation du point ou un tableau contenant le keyPath pour les références profondes.
Vous pouvez fournir un argument facultatif value
pour comparer la valeur de la propriété reçue (de manière récursive pour toutes les propriétés des instances d'objets, également appelée égalité profonde, comme le comparateur 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 dans l'objet.
// Objet contenant les fonctionnalités de la maison à tester
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('cette maison a mes fonctionnalités désirées', () => {
// Exemple de référencement
expect(houseForSale).toHaveProperty('bath');
expect(houseForSale).toHaveProperty('bedrooms', 4);
expect(houseForSale).not.toHaveProperty('pool');
// Référencement profond à l'aide de la notation par points
expect(houseForSale).toHaveProperty('kitchen.area', 20);
expect(houseForSale).toHaveProperty('kitchen.amenities', [
'oven',
'stove',
'washer',
]);
expect(houseForSale).not.toHaveProperty('kitchen.open');
// Référencement profond en utilisant un tableau contenant le 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 les nombres à virgule flottante pour une égalité approximative.
L'argument optionnel numDigits
limite le nombre de chiffres à vérifier après le point décimal. Pour la valeur par défaut 2
, le critère de test est Math.abs(expected - received) < 0.005
(c'est-à-dire 10 ** -2 / 2
).
Les comparaisons intuitives d'égalité échouent souvent, car l'arithmétique sur les valeurs décimales (base 10) comporte souvent des erreurs d'arrondi dans la représentation binaire avec une précision limitée (base 2). Par exemple, ce test échoue :
test('l\'addition fonctionne sainement avec les décimales', () => {
expect(0.2 + 0.1).toBe(0.3); // échoue !
});
Il échoue car en JavaScript, 0.2 + 0.1
est en fait 0.3000000000004
.
Par exemple, ce test passe avec une précision de 5 chiffres :
test('l\'addition fonctionne sainement avec les décimales', () => {
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
});
Comme les erreurs en virgule flottante sont le problème que toBeCloseTo
résout, il ne supporte pas les grandes valeurs entières.
.toBeDefined()
Utilisez .toBeDefined
pour vérifier qu'une variable n'est pas undefined. Par exemple, si vous voulez vérifier qu'une fonction fetchNewFlavorIdea()
retourne quelque chose, vous pouvez écrire :
test('il y a une nouvelle idée de saveur', () => {
expect(fetchNewFlavorIdea()).toBeDefined();
});
Vous pourriez écrire expect(fetchNewFlavorIdea()).not.toBe(undefined)
, mais il est préférable d'éviter de faire référence à undefined
directement dans votre code.
.toBeFalsy()
Utilisez .toBeFalsy
lorsque vous ne vous souciez pas de ce qu'est une valeur et que vous voulez vous assurer qu'une valeur est fausse dans un contexte booléen. Par exemple, disons que vous avez un code d'application qui ressemble à ceci :
drinkSomeLaCroix();
if (!getErrors()) {
drinkMoreLaCroix();
}
Vous pouvez ne pas vous soucier de ce que getErrors
renvoie, spécifiquement - il pourrait retourner false
, null
, ou 0
, et votre code fonctionnerait toujours. Donc, si vous voulez tester qu'il n'y a pas d'erreurs après avoir consommé La Croix, vous pouvez écrire :
test('drinking La Croix ne conduit pas à des erreurs', () => {
drinkSomeLaCroix();
expect(getErrors()).toBeFalsy();
});
En JavaScript, il y a six valeurs fausses : false
, 0
, ''
, null
, undefined
et NaN
. Tout le reste est vrai.
.toBeGreaterThan(number | bigint)
Utilisez toBeGreaterThan
pour comparer reçu > attendu
pour des valeurs numériques ou de grands entiers. Par exemple, tester que ouncesPerCan()
retourne une valeur de plus de 10 onces :
test('ounces per can est plus grand que 10', () => {
expect(ouncesPerCan()).toBeGreaterThan(10);
});
.toBeGreaterThanOrEqual(number | bigint)
Utilisez toBeGreaterThanOrEqual
pour comparer reçu >= attendu
pour des valeurs numériques ou de grands entiers. Par exemple, tester que ouncesPerCan()
retourne une valeur d'au moins 12 onces :
test('ounces per can est au moins plus grand que 12', () => {
expect(ouncesPerCan()).toBeGreaterThanOrEqual(12);
});
.toBeLessThan(number | bigint)
Utilisez toBeLessThan
pour comparer reçu < attendu
pour des valeurs numériques ou de grands entiers. Par exemple, tester que ouncesPerCan()
retourne une valeur de moins de 20 onces :
test('ounces per can est inférieur à 20', () => {
expect(ouncesPerCan()).toBeLessThan(20);
});
.toBeLessThanOrEqual(number | bigint)
Utilisez toBeLessThanOrEqual
pour comparer reçu <= attendu
pour des valeurs numériques ou de grands entiers. Par exemple, tester que ouncesPerCan()
retourne une valeur d'au plus 12 onces :
test('ounces per can est au plus de 12', () => {
expect(ouncesPerCan()).toBeLessThanOrEqual(12);
});
.toBeInstanceOf(Class)
Utilisez .toBeInstanceOf(Class)
pour vérifier qu'un objet est une instance d'une classe. Ce comparateur utilise instanceof
en dessous.
class A {}
expect(new A()).toBeInstanceOf(A);
expect(() => {}).toBeInstanceOf(Function);
expect(new A()).toBeInstanceOf(Function); // throws
.toBeNull()
.toBeNull()
est identique à .toBe(null)
mais les messages d'erreur sont un peu plus jolis. Utilisez donc .toBeNull()
lorsque vous voulez vérifier que quelque chose est null.
function bloop() {
return null;
}
test('bloop retourne null', () => {
expect(bloop()).toBeNull();
});
.toBeTruthy()
Utilisez .toBeTruthy
lorsque vous ne vous souciez pas de ce qu'est une valeur et que vous voulez vous assurer qu'une valeur est vraie dans un contexte booléen. Par exemple, disons que vous avez un code d'application qui ressemble à ceci :
drinkSomeLaCroix();
if (thirstInfo()) {
drinkMoreLaCroix();
}
Vous pouvez ne pas vous soucier de ce que thirstInfo
renvoie, spécifiquement - il pourrait retourner true
ou un objet complexe, et votre code fonctionnerait toujours. Ainsi, si vous voulez tester que thirstInfo
sera vrai après avoir bu du La Croix, vous pouvez écrire :
test('boire du La Croix conduit à avoir des infos sur la soif', () => {
drinkSomeLaCroix();
expect(thirstInfo()).toBeTruthy();
});
En JavaScript, il y a six valeurs fausses : false
, 0
, ''
, null
, undefined
et NaN
. Tout le reste est vrai.
.toBeUndefined()
Utilisez .toBeUndefined
pour vérifier qu'une variable est undefined. Par exemple, si vous voulez vérifier qu'une fonction bestDrinkForFlavor(flavor)
renvoie undefined
pour la saveur 'octopus'
, car il n'existe pas de bonne boisson aromatisée à l'octopus :
test('la meilleure boisson pour la saveur octopus est undefined', () => {
expect(bestDrinkForFlavor('octopus')).toBeUndefined();
});
Vous pourriez écrire expect(bestDrinkForFlavor('octopus')).toBe(undefined)
, mais il est préférable d'éviter de faire référence à undefined
directement dans votre code.
.toBeNaN()
Utilisez .toBeNaN
lorsque vous vérifiez qu'une valeur est NaN
.
test('passe lorsque la valeur est NaN', () => {
expect(NaN).toBeNaN();
expect(1).not.toBeNaN();
});
.toContain(item)
Utilisez .toContain
lorsque vous voulez vérifier qu'un élément est dans un tableau. Pour tester les éléments du tableau, on utilise ===
, un contrôle d'égalité strict. .toContain
peut également vérifier si une chaîne de caractères est une sous-chaîne d'une autre chaîne.
Par exemple, si getAllFlavors()
retourne un tableau de saveurs et vous voulez être sûr que lime
est là, vous pouvez écrire :
test('la liste de saveurs contient lime', () => {
expect(getAllFlavors()).toContain('lime');
});
Ce comparateur accepte également d'autres itérables tels que les chaînes, les sets, les listes de noeuds et les collections HTML.
.toContainEqual(item)
Utilisez .toContainEqual
lorsque vous voulez vérifier qu'un élément ayant une structure et des valeurs spécifiques est contenu dans un tableau. Pour tester les éléments du tableau, ce comparateur vérifie récursivement l'égalité de tous les champs, plutôt que de vérifier l'identité des objets.
describe('ma boisson', () => {
test('est délicieuse et non acide', () => {
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 connu sous le nom d'égalité « profonde »). Il appelle Object.is
pour comparer des valeurs primitives, ce qui est encore mieux pour les tests que l'opérateur d'égalité stricte ===
.
Par exemple, .toEqual
et .toBe
se comportent différemment dans cette suite de tests, de sorte que tous les tests passent :
const can1 = {
flavor: 'grapefruit',
ounces: 12,
};
const can2 = {
flavor: 'grapefruit',
ounces: 12,
};
describe('les canettes de La Croix sur mon bureau', () => {
test('ont toutes les mêmes propriétés', () => {
expect(can1).toEqual(can2);
});
test('ne sont pas exactement les mêmes canettes', () => {
expect(can1).not.toBe(can2);
});
});
toEqual
ignore les clés d'objets avec des propriétés undefined
, les éléments undefined
dans les tableaux, les trous dans les tableaux, ou les incompatibilités de types d'objets. To take these into account use .toStrictEqual
instead.
.toEqual
n'effectuera pas de vérification d'égalité profonde pour deux erreurs. Seule la propriété message
d'une erreur est prise en compte pour l'égalité. Il est recommandé d'utiliser le comparateur .toThrow
pour les tests d'erreurs.
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, utilisez la méthode equals
de la classe Buffer
pour vérifier si les buffers contiennent ou non le même contenu :
- rewrite
expect(received).toEqual(expected)
asexpect(received.equals(expected)).toBe(true)
- rewrite
expect(received).not.toEqual(expected)
asexpect(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 ne savez peut-être pas ce que essayOnTheBestFlavor()
renvoie exactement, mais vous savez qu'il s'agit d'une très longue chaîne de caractères et que la sous-chaîne grapefruit
devrait s'y trouver quelque part. Vous pouvez tester ceci avec :
describe('un essai sur la meilleure saveur', () => {
test('mentionne grapefruit', () => {
expect(essayOnTheBestFlavor()).toMatch(/grapefruit/);
expect(essayOnTheBestFlavor()).toMatch(new RegExp('grapefruit'));
});
});
Ce comparateur accepte également une chaîne de caractères, qu'il essaiera de faire correspondre :
describe('grapefruits sont sains', () => {
test('grapefruits sont des fruits', () => {
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 objet. Il correspondra aux objets reçus dont les propriétés ne sont pas dans l'objet attendu.
Vous pouvez également passer un tableau d'objets, auquel cas la méthode retournera true uniquement si chaque objet du tableau reçu correspond (au sens toMatchObject
décrit ci-dessus) à l'objet correspondant dans le tableau attendu. Ceci est utile si vous voulez vérifier que deux tableaux correspondent dans leur nombre d'éléments, par opposition à arrayContaining
, qui autorise des éléments supplémentaires dans le tableau reçu.
Vous pouvez faire correspondre des propriétés à des valeurs ou à des comparateurs.
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('la maison a mes caractéristiques souhaitées', () => {
expect(houseForSale).toMatchObject(desiredHouse);
});
describe('toMatchObject appliqué aux tableaux', () => {
test('le nombre d\'éléments doit correspondre exactement', () => {
expect([{foo: 'bar'}, {baz: 1}]).toMatchObject([{foo: 'bar'}, {baz: 1}]);
});
test('.toMatchObject est appelé pour chaque élément, donc les propriétés supplémentaires de l\'objet sont correctes', () => {
expect([{foo: 'bar'}, {baz: 1, extra: 'quux'}]).toMatchObject([
{foo: 'bar'},
{baz: 1},
]);
});
});
.toMatchSnapshot(propertyMatchers?, hint?)
Cela garantit qu'une valeur correspond au snapshot le plus récent. Consultez le guide de test des snapshots pour plus d'informations.
Vous pouvez fournir un argument objet optionnel propertyMatchers
, qui a des comparateurs asymétriques comme valeurs d'un sous-ensemble de propriétés attendues, si la valeur reçue est une instance d'objet. C'est comme toMatchObject
avec des critères flexibles pour un sous-ensemble de propriétés, suivis d'un test de snapshot comme critère exact pour le reste des propriétés.
Vous pouvez fournir un argument hint
facultatif de type string qui sera ajouté au nom du test. Bien que Jest ajoute toujours un numéro à la fin du nom d'un snapshot, de courtes indications descriptives pourraient ê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)
Assure qu'une valeur correspond au snapshot le plus récent.
Vous pouvez fournir un argument objet optionnel propertyMatchers
, qui a des comparateurs asymétriques comme valeurs d'un sous-ensemble de propriétés attendues, si la valeur reçue est une instance d'objet. C'est comme toMatchObject
avec des critères flexibles pour un sous-ensemble de propriétés, suivis d'un test de snapshot comme critère exact pour le reste des propriétés.
Jest ajoute l'argument inlineSnapshot
au comparateur 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 snapshots en ligne pour plus d'informations.
.toStrictEqual(value)
Use .toStrictEqual
to test that objects have the same structure and type.
Différences avec .toEqual
:
- keys with
undefined
properties are checked, e.g.{a: undefined, b: 2}
will not equal{b: 2}
; undefined
items are taken into account, e.g.[2]
will not equal[2, undefined]
;- array sparseness is checked, e.g.
[, 1]
will not equal[undefined, 1]
; - object types are checked, e.g. a class instance with fields
a
andb
will not equal a literal object with fieldsa
andb
.
class LaCroix {
constructor(flavor) {
this.flavor = flavor;
}
}
describe('les canettes La Croix sur mon bureau', () => {
test('ne sont pas sémantiquement les mêmes', () => {
expect(new LaCroix('lemon')).toEqual({flavor: 'lemon'});
expect(new LaCroix('lemon')).not.toStrictEqual({flavor: 'lemon'});
});
});
.toThrow(error?)
Aussi sous l'alias : .toThrowError(error?)
Utilisez .toThrow
pour tester qu'une fonction lève une exception lorsqu'elle est appelée. Par exemple, si nous voulons tester que drinkFlavor('octopus')
lève une exception, parce que la saveur de octopus est trop dégoûtante à boire, nous pourrions écrire :
test('throws sur octopus', () => {
expect(() => {
drinkFlavor('octopus');
}).toThrow();
});
Vous devez envelopper le code dans une fonction, sinon l'erreur ne sera pas détectée et l'assertion échouera.
Vous pouvez fournir un argument facultatif pour vérifier qu'une erreur spécifique est déclenchée :
- regular expression: error message matches the pattern
- string: error message includes the substring
- error object: error message is equal to the message property of the object
- error class: error object is instance of class
Par exemple, supposons que le drinkFlavor
est codé comme suit :
function drinkFlavor(flavor) {
if (flavor == 'octopus') {
throw new DisgustingFlavorError('beurk, goût octopus');
}
// Faire d'autres choses
}
Nous pourrions tester cette erreur de plusieurs façons :
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 tester qu'une fonction lance une erreur correspondant au snapshot le plus récent lorsqu'il est appelé.
Vous pouvez fournir un argument hint
facultatif de type string qui sera ajouté au nom du test. Bien que Jest ajoute toujours un numéro à la fin du nom d'un snapshot, de courtes indications descriptives pourraient ê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 avez une fonction drinkFlavor
qui lève une exception chaque fois que la saveur est 'octopus'
, et qui est codée comme ceci :
function drinkFlavor(flavor) {
if (flavor == 'octopus') {
throw new DisgustingFlavorError('beurk, goût octopus');
}
// Faire d'autres choses
}
Le test pour cette fonction se présentera de la manière suivante :
test('throws on octopus', () => {
function drinkOctopus() {
drinkFlavor('octopus');
}
expect(drinkOctopus).toThrowErrorMatchingSnapshot();
});
Et cela va générer le snapshot suivant :
exports[`drinking flavors throws on octopus 1`] = `"beurk, goût octopus"`;
Consultez Test de snapshot de l'arborescence React pour plus d'informations sur les tests de snapshots.
.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)
Utilisez .toThrowErrorMatchingInlineSnapshot
pour tester qu'une fonction lève une erreur correspondant au snapshot le plus récent lorsqu'elle est appelée.
Jest ajoute l'argument inlineSnapshot
au comparateur 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 snapshots en ligne pour plus d'informations.
Asymmetric Matchers
expect.anything()
expect.anything()
correspond à tout sauf à null
ou undefined
. You can use it inside toEqual
or toHaveBeenCalledWith
instead of a literal value. Par exemple, si vous voulez vérifier qu'une fonction simulée est appelée avec un argument non null :
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 ce qui a été créé avec le constructeur donné ou si c'est une primitive qui est du type transmis. You can use it inside toEqual
or toHaveBeenCalledWith
instead of a literal value. Par exemple, si vous voulez vérifier qu'une fonction simulée 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 qui contient tous les éléments du tableau attendu. C'est-à-dire que le tableau attendu est un sous-ensemble du tableau reçu. Par conséquent, il correspond à un tableau reçu qui contient des éléments qui ne sont pas dans le tableau attendu.
Vous pouvez l'utiliser à la place d'une valeur littérale :
- in
toEqual
ortoHaveBeenCalledWith
- to match a property in
objectContaining
ortoMatchObject
describe('arrayContaining', () => {
const expected = ['Alice', 'Bob'];
it('correspond même si celui reçu contient des éléments supplémentaires', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(expect.arrayContaining(expected));
});
it('ne correspond pas si celui reçu ne contient pas les éléments attendus', () => {
expect(['Bob', 'Eve']).not.toEqual(expect.arrayContaining(expected));
});
});
describe('Méfiez-vous d\'un malentendu ! Une séquence de lancers de dés', () => {
const expected = [1, 2, 3, 4, 5, 6];
it('correspond même avec un nombre inattendu, le 7', () => {
expect([4, 1, 6, 7, 3, 5, 2, 5, 4, 6]).toEqual(
expect.arrayContaining(expected),
);
});
it('ne correspond pas sans un nombre attendu, le 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. C'est-à-dire que 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('correspond si le tableau actuel ne contient pas les éléments attendus', () => {
expect(['Alice', 'Bob', 'Eve']).toEqual(
expect.not.arrayContaining(expected),
);
});
});
expect.closeTo(number, numDigits?)
expect.closeTo(number, numDigits?)
est utile lors de la comparaison de nombres à virgule flottante dans les propriétés de l'objet ou d'élément de tableau. Si vous avez besoin de comparer un nombre, veuillez utiliser .toBeCloseTo
à la place.
L'argument optionnel numDigits
limite le nombre de chiffres à vérifier après le point décimal. Pour la valeur par défaut 2
, le critère de test est Math.abs(expected - received) < 0.005 (that is, 10 ** -2 / 2)
.
Par exemple, ce test passe avec une précision de 5 chiffres :
test('compare un nombre décimal dans les propriétés de l\'objet', () => {
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 qui correspond récursivement aux propriétés attendues. C'est-à-dire que l'objet attendu est un sous-ensemble de l'objet reçu. Par conséquent, il correspond à un objet reçu qui contient des propriétés qui sont présentes dans l'objet attendu.
Au lieu de valeurs de propriétés littérales dans l'objet attendu, vous pouvez utiliser des comparateurs, expect.anything()
, et ainsi de suite.
Par exemple, disons que nous nous attendons à ce qu'une fonction onPress
soit appelée avec un objet Event
, et tout ce que nous devons vérifier est que l'événement a les propriétés event.x
et event.y
. Nous pouvons le tester ainsi :
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 qui ne correspond pas récursivement aux propriétés attendues. C'est-à-dire que l'objet attendu n'est pas un sous-ensemble de l'objet reçu. Par conséquent, il correspond à un objet reçu qui contient des propriétés qui ne sont pas dans l'objet attendu.
C'est l'inverse de expect.objectContaining
.
describe('not.objectContaining', () => {
const expected = {foo: 'bar'};
it('correspond si l\'objet réel ne contient pas les paires clé-valeur attendues', () => {
expect({bar: 'baz'}).toEqual(expect.not.objectContaining(expected));
});
});
expect.stringContaining(string)
expect.stringContaining(string)
correspond à la valeur reçue si c'est une chaîne qui contient exactement la chaîne attendue.
expect.not.stringContaining(string)
expect.not.stringContaining(string)
correspond à la valeur reçue si ce n'est pas une chaîne ou si c'est une chaîne qui ne contient pas la chaîne exacte attendue.
C'est l'inverse de expect.stringContaining
.
describe('not.stringContaining', () => {
const expected = 'Hello world!';
it('correspond si la valeur reçue ne contient pas la sous-chaîne attendue', () => {
expect('How are you?').toEqual(expect.not.stringContaining(expected));
});
});
expect.stringMatching(string | regexp)
expect.stringMatching(string | regexp)
correspond à la valeur reçue si c'est une chaîne qui correspond à la chaîne attendue ou à l'expression régulière.
Vous pouvez l'utiliser à la place d'une valeur littérale :
- in
toEqual
ortoHaveBeenCalledWith
- to match an element in
arrayContaining
- to match a property in
objectContaining
ortoMatchObject
Cet exemple montre également comment vous pouvez imbriquer plusieurs comparateurs asymétriques, avec expect.stringMatching
à l'intérieur du expect.arrayContaining
.
describe('stringMatching in arrayContaining', () => {
const expected = [
expect.stringMatching(/^Alic/),
expect.stringMatching(/^[BR]ob/),
];
it('correspond même si celle reçue contient des éléments supplémentaires', () => {
expect(['Alicia', 'Roberto', 'Evelina']).toEqual(
expect.arrayContaining(expected),
);
});
it('ne correspond pas si celle reçue ne contient pas les éléments attendus', () => {
expect(['Roberto', 'Evelina']).not.toEqual(
expect.arrayContaining(expected),
);
});
});
expect.not.stringMatching(string | regexp)
expect.not.stringMatching(string | regexp)
correspond à la valeur reçue si ce n'est pas une chaîne ou si c'est une chaîne qui ne correspond pas à la chaîne ou à l'expression régulière attendue.
C'est l'inverse de expect.stringMatching
.
describe('not.stringMatching', () => {
const expected = /Hello world!/;
it('correspond si la valeur reçue ne correspond pas à la regex attendue', () => {
expect('How are you?').toEqual(expect.not.stringMatching(expected));
});
});
Assertion Count
expect.assertions(number)
expect.assertions(number)
vérifie qu'un certain nombre d'assertions sont appelées lors d'un test. Ceci est souvent utile lors des tests de code asynchrone, afin de s'assurer que les assertions dans un callback ont été effectivement appelées.
Par exemple, supposons que nous ayons une fonction doAsync
qui reçoit deux callbacks callback1
et callback2
, elle va les appeler de manière asynchrone dans un ordre inconnu. Nous pouvons le tester ainsi :
test('doAsync appelle les deux 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 effectivement appelés.
expect.hasAssertions()
expect.hasAssertions()
vérifie qu'au moins une assertion est appelée lors d'un test. Ceci est souvent utile lors des tests de code asynchrone, afin de s'assurer que les assertions dans un callback ont été effectivement appelées.
Par exemple, disons que nous avons quelques fonctions qui traitent toutes de l'état. prepareState
appelle une callback avec un objet d'état, validateState
s'exécute sur cet objet d'état, et waitOnState
renvoie une promesse qui attend que toutes les callbacks de prepareState
se terminent. Nous pouvons le tester ainsi :
test('prepareState prépare un état valide', () => {
expect.hasAssertions();
prepareState(state => {
expect(validateState(state)).toBeTruthy();
});
return waitOnState();
});
L'appel expect.hasAssertions()
garantit que la callback prepareState
est effectivement appelée.
Extend Utilities
expect.addEqualityTesters(testers)
You can use expect.addEqualityTesters
to add your own methods to test if two objects are equal. For example, let's say you have a class in your code that represents volume and can determine if two volumes using different units are equal. You may want toEqual
(and other equality matchers) to use this custom equality method when comparing to Volume classes. You can add a custom equality tester to have toEqual
detect and apply custom logic when comparing Volume classes:
- JavaScript
- TypeScript
// 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;
}
}
}
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]);
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'));
});
// For simplicity in this example, we'll just support the units 'L' and 'mL'
export class Volume {
public amount: number;
public unit: 'L' | 'mL';
constructor(amount: number, unit: 'L' | 'mL') {
this.amount = amount;
this.unit = unit;
}
toString(): string {
return `[Volume ${this.amount}${this.unit}]`;
}
equals(other: Volume): boolean {
if (this.unit === other.unit) {
return this.amount === other.amount;
} else if (this.unit === 'L' && other.unit === 'mL') {
return this.amount * 1000 === other.amount;
} else {
return this.amount === other.amount * 1000;
}
}
}
import {expect} from '@jest/globals';
import {Volume} from './Volume.js';
function areVolumesEqual(a: unknown, b: unknown): boolean | undefined {
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]);
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'));
});
Custom equality testers API
Custom testers are functions that return either the result (true
or false
) of comparing the equality of the two given arguments or undefined
if the tester does not handle the given objects and wants to delegate equality to other testers (for example, the builtin equality testers).
Custom testers are called with 3 arguments: the two objects to compare and the array of custom testers (used for recursive testers, see the section below).
These helper functions and properties can be found on this
inside a custom tester:
this.equals(a, b, customTesters?)
Il s'agit d'une fonction d'égalité profonde qui retournera true
si deux objets ont les mêmes valeurs (de manière récursive). It optionally takes a list of custom equality testers to apply to the deep equality checks. If you use this function, pass through the custom testers your tester is given so further equality checks equals
applies can also use custom testers the test author may have configured. See the example in the Recursive custom equality testers section for more details.
Matchers vs Testers
Matchers are methods available on expect
, for example expect().toEqual()
. toEqual
is a matcher. A tester is a method used by matchers that do equality checks to determine if objects are the same.
Custom matchers are good to use when you want to provide a custom assertion that test authors can use in their tests. For example, the toBeWithinRange
example in the expect.extend
section is a good example of a custom matcher. Sometimes a test author may want to assert two numbers are exactly equal and should use toBe
. Other times, however, a test author may want to allow for some flexibility in their test, and toBeWithinRange
may be a more appropriate assertion.
Custom equality testers are good for globally extending Jest matchers to apply custom equality logic for all equality comparisons. Test authors can't turn on custom testers for certain assertions and turn them off for others (a custom matcher should be used instead if that behavior is desired). For example, defining how to check if two Volume
objects are equal for all matchers would be a good custom equality tester.
Recursive custom equality testers
If your custom equality testers are testing objects with properties you'd like to do deep equality with, you should use the this.equals
helper available to equality testers. This equals
method is the same deep equals method Jest uses internally for all of its deep equality comparisons. It's the method that invokes your custom equality tester. It accepts an array of custom equality testers as a third argument. Custom equality testers are also given an array of custom testers as their third argument. Pass this argument into the third argument of equals
so that any further equality checks deeper into your object can also take advantage of custom equality testers.
For example, let's say you have a Book
class that contains an array of Author
classes and both of these classes have custom testers. The Book
custom tester would want to do a deep equality check on the array of Author
s and pass in the custom testers given to it, so the Author
s custom equality tester is applied:
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]);
Remember to define your equality testers as regular functions and not arrow functions in order to access the tester context helpers (e.g. this.equals
).
expect.addSnapshotSerializer(serializer)
Vous pouvez appeler expect.addSnapshotSerializer
pour ajouter un module qui formate les structures de données spécifiques à l'application.
Pour un fichier de test individuel, un module ajouté précède tous les modules de la configuration snapshotSerializers
, qui précèdent les sérialiseurs snapshot par défaut pour les types JavaScript intégrés et pour les éléments React. Le dernier module ajouté est le premier module testé.
import serializer from 'my-serializer-module';
expect.addSnapshotSerializer(serializer);
// affecte les assertions expect(value).toMatchSnapshot() dans le fichier de test
Si vous ajoutez un sérialiseur de snapshot dans des fichiers de test individuels au lieu de l'ajouter à la configuration snapshotSerializers
:
- You make the dependency explicit instead of implicit.
- You avoid limits to configuration that might cause you to eject from create-react-app.
Consultez la configuration de Jest pour plus d'informations.
expect.extend(matchers)
Vous pouvez utiliser expect.extend
pour ajouter vos propres comparateurs à Jest. Par exemple, supposons que vous testiez une bibliothèque d'utilitaires de nombres et que vous vérifiiez fréquemment que les nombres apparaissent dans des plages particulières de nombres. Vous pourriez résumer cela en un comparateur toBeWithinRange
:
- JavaScript
- TypeScript
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,
});
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),
});
});
// 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 {};
import {expect} from '@jest/globals';
import type {MatcherFunction} from 'expect';
const toBeWithinRange: MatcherFunction<[floor: unknown, ceiling: unknown]> =
// `floor` and `ceiling` get types from the line above
// it is recommended to type them as `unknown` and to validate the values
function (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: () =>
// `this` context will have correct typings
`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,
});
declare module 'expect' {
interface AsymmetricMatchers {
toBeWithinRange(floor: number, ceiling: number): void;
}
interface Matchers<R> {
toBeWithinRange(floor: number, ceiling: number): R;
}
}
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),
});
});
The type declaration of the matcher can live in a .d.ts
file or in an imported .ts
module (see JS and TS examples above respectively). If you keep the declaration in a .d.ts
file, make sure that it is included in the program and that it is a valid module, i.e. it has at least an empty export {}
.
Instead of importing toBeWithinRange
module to the test file, you can enable the matcher for all tests by moving the expect.extend
call to a setupFilesAfterEnv
script:
import {expect} from '@jest/globals';
// remember to export `toBeWithinRange` as well
import {toBeWithinRange} from './toBeWithinRange';
expect.extend({
toBeWithinRange,
});
Async Matchers
expect.extend
prend également en charge lescomparateurs asynchrones. Les comparateurs asynchrones renvoient une promesse. Vous devrez donc attendre la valeur renvoyée. Utilisons un exemple de comparateur pour illustrer leur utilisation. Nous allons implémenter un comparateur appelé toBeDivisibleByExternalValue
, où le nombre divisible va être obtenu à partir d'une source externe.
expect.extend({
async toBeDivisibleByExternalValue(received) {
const externalValue = await getExternalValueFromRemoteSource();
const pass = received % externalValue == 0;
if (pass) {
return {
message: () =>
`s'attend à ce que ${received} ne soit pas divisible par ${externalValue}`,
pass: true,
};
} else {
return {
message: () =>
`s'attend à ce que ${received} soit divisible par ${externalValue}`,
pass: false,
};
}
},
});
test('est divisible par une valeur externe', async () => {
await expect(100).toBeDivisibleByExternalValue();
await expect(101).not.toBeDivisibleByExternalValue();
});
Custom Matchers API
Les comparateurs doivent retourner un objet (ou une promesse d'un objet) avec deux clés. pass
indique s'il y a eu ou non une correspondance, et message
fournit une fonction sans arguments qui retourne un message d'erreur en cas d'échec. Ainsi, lorsque pass
est faux, message
devrait retourner le message d'erreur lorsque expect(x).yourMatcher()
échoue. Et lorsque pass
est vrai, message
devrait retourner le message d'erreur lorsque expect(x).not.yourMatcher()
échoue.
Les comparateurs 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 peuvent être trouvées sur this
à l'intérieur d'un comparateur personnalisé :
this.isNot
Un booléen pour vous indiquer que ce comparateur a été appelé avec le modificateur de négation .not
, ce qui vous permet d'afficher une indication claire et correcte du comparateur (voir l'exemple de code).
this.promise
Une chaîne permettant d'afficher un indice de concordance clair et correct :
'rejects'
if matcher was called with the promise.rejects
modifier'resolves'
if matcher was called with the promise.resolves
modifier''
if matcher was not called with a promise modifier
this.equals(a, b, customTesters?)
Il s'agit d'une fonction d'égalité profonde qui retournera true
si deux objets ont les mêmes valeurs (de manière récursive). It optionally takes a list of custom equality testers to apply to the deep equality checks (see this.customTesters
below).
this.expand
Un booléen pour vous indiquer que ce comparateur a été appelé avec une option expand
. Quand Jest est appelé avec le flag --expand
, this.expand
peut être utilisé pour déterminer si Jest est censé afficher des différences et des erreurs complètes.
this.utils
There are a number of helpful tools exposed on this.utils
primarily consisting of the exports from jest-matcher-utils
.
Les plus utiles sont matcherHint
, printExpected
et printReceived
pour formater joliment les messages d'erreur. Par exemple, jetez un œil à l'implémentation pour le comparateur 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 autant de détails que nécessaire à l'utilisateur pour qu'il puisse résoudre son problème rapidement. Vous devez élaborer un message d'échec précis pour vous assurer que les utilisateurs de vos assertions personnalisées ont une bonne expérience de développeur.
this.customTesters
If your matcher does a deep equality check using this.equals
, you may want to pass user-provided custom testers to this.equals
. The custom equality testers the user has provided using the addEqualityTesters
API are available on this property. The built-in Jest matchers pass this.customTesters
(along with other built-in testers) to this.equals
to do deep equality, and your custom matchers may want to do the same.
Custom snapshot matchers
Pour utiliser les tests de snapshot à l'intérieur de votre comparateur personnalisé, vous pouvez importer jest-snapshot
et l'utiliser depuis votre comparateur.
Voici un comparateur de snapshot qui découpe une chaîne de caractères à stocker pour 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 également possible de créer des comparateurs personnalisés pour les snapshots en ligne, les snapshots seront correctement ajoutés aux comparateurs personnalisés. Cependant, le snapshot en ligne essaiera toujours d'ajouter au premier argument ou au second lorsque le premier argument est le comparateur de propriétés, il n'est donc pas possible d'accepter des arguments personnalisés dans les comparateurs personnalisés.
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"`
);
*/
});
async
Si votre comparateur de snapshots en ligne personnalisé est asynchrone, c'est-à-dire qu'il utilise async
-await
, vous pouvez rencontrer une erreur du type "Multiple inline snapshots for the same call are not supported". Jest a besoin d'informations contextuelles supplémentaires pour trouver où le comparateur de snapshot en ligne personnalisé a été utilisé pour mettre à jour les snapshots correctement.
const {toMatchInlineSnapshot} = require('jest-snapshot');
expect.extend({
async toMatchObservationInlineSnapshot(fn, ...rest) {
// L'erreur (et sa stacktrace) doit être créée avant tout `await`.
this.error = new Error();
// L'implémentation de `observe` n'a pas d'importance.
// Il importe seulement que le comparateur de snapshots personnalisé soit asynchrone.
const observation = await observe(async () => {
await fn();
});
return toMatchInlineSnapshot.call(this, recording, ...rest);
},
});
it('observe quelque chose', async () => {
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot();
/*
Le snapshot sera ajouté en ligne comme suit
await expect(async () => {
return 'async action';
}).toMatchTrimmedInlineSnapshot(`"async action"`);
*/
});
Bail out
Habituellement, jest
essaie de faire correspondre chaque snapshot qui est attendu dans un test.
Parfois, il peut être inutile de poursuivre le test si un snapshot précédent a échoué. Par exemple, lorsque vous faites des snapshots d'un état-machine après diverses transitions, vous pouvez interrompre le test dès qu'une transition produit un état erroné.
Dans ce cas, vous pouvez mettre en place un comparateur de snapshots personnalisé qui se déclenche à la première incohérence au lieu de collecter toutes les incohérences.
const {toMatchInlineSnapshot} = require('jest-snapshot');
expect.extend({
toMatchStateInlineSnapshot(...args) {
this.dontThrow = () => {};
return toMatchInlineSnapshot.call(this, ...args);
},
});
let state = 'initial';
function transition() {
// Une erreur dans l'implémentation devrait faire échouer le test.
if (state === 'INITIAL') {
state = 'pending';
} else if (state === 'pending') {
state = 'done';
}
}
it('transitions comme prévu', () => {
expect(state).toMatchStateInlineSnapshot(`"initial"`);
transition();
// Cela produit déjà un dysfonctionnement. Il est inutile de poursuivre le test.
expect(state).toMatchStateInlineSnapshot(`"loading"`);
transition();
expect(state).toMatchStateInlineSnapshot(`"done"`);
});