Zum Hauptinhalt springen
Version: 29.7

Erwarten

Inoffizielle Beta-Übersetzung

Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →

Beim Schreiben von Tests müssen Sie häufig prüfen, ob Werte bestimmte Bedingungen erfüllen. expect bietet Zugriff auf verschiedene "Matcher", mit denen Sie unterschiedliche Dinge validieren können.

Tipp

Zusätzliche Jest-Matcher, die von der Community gepflegt werden, finden Sie unter jest-extended.

Inoffizielle Beta-Übersetzung

Diese Seite wurde von PageTurner AI übersetzt (Beta). Nicht offiziell vom Projekt unterstützt. Fehler gefunden? Problem melden →

Hinweis

Die TypeScript-Beispiele auf dieser Seite funktionieren nur wie dokumentiert, wenn Sie die Jest-APIs explizit importieren:

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

Weitere Details zur Einrichtung von Jest mit TypeScript finden Sie im Leitfaden Erste Schritte.

Referenz


Erwarten

expect(value)

Die expect-Funktion wird immer dann verwendet, wenn Sie einen Wert testen möchten. Sie werden expect selten alleine aufrufen. Stattdessen kombinieren Sie expect mit einer "Matcher"-Funktion, um Aussagen über einen Wert zu treffen.

Ein Beispiel macht dies verständlicher: Angenommen, Sie haben eine Methode bestLaCroixFlavor(), die den String 'grapefruit' zurückgeben soll. So würden Sie das testen:

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

Hier ist toBe die Matcher-Funktion. Es gibt viele verschiedene Matcher-Funktionen, die unten dokumentiert sind und Ihnen helfen, unterschiedliche Dinge zu testen.

Das Argument für expect sollte der von Ihrem Code erzeugte Wert sein, und jedes Argument für den Matcher sollte der korrekte Vergleichswert sein. Wenn Sie diese vertauschen, funktionieren Ihre Tests zwar, aber die Fehlermeldungen bei fehlgeschlagenen Tests wirken merkwürdig.

Modifikatoren

.not

Wenn Sie wissen, wie man etwas testet, ermöglicht .not Ihnen, das Gegenteil zu prüfen. Dieser Code testet beispielsweise, dass der beste La Croix-Geschmack nicht Kokosnuss ist:

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

.resolves

Verwenden Sie resolves, um den Wert eines erfüllten Promises zu entpacken, sodass jeder andere Matcher angehängt werden kann. Wenn das Promise abgelehnt wird, schlägt die Assertion fehl.

Dieser Code testet beispielsweise, dass das Promise erfolgreich ist und der resultierende Wert 'lemon' ist:

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

Da Sie weiterhin Promises testen, bleibt der Test asynchron. Daher müssen Sie Jest anweisen zu warten, indem Sie die entpackte Assertion zurückgeben.

Alternativ können Sie async/await in Kombination mit .resolves verwenden:

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

.rejects

Verwenden Sie .rejects, um den Ablehnungsgrund eines Promises zu entpacken, sodass jeder andere Matcher angehängt werden kann. Wenn das Promise erfüllt wird, schlägt die Assertion fehl.

Dieser Code testet beispielsweise, dass das Promise mit dem Grund 'octopus' abgelehnt wird:

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

Da Sie weiterhin Promises testen, bleibt der Test asynchron. Daher müssen Sie Jest anweisen zu warten, indem Sie die entpackte Assertion zurückgeben.

Alternativ können Sie async/await in Kombination mit .rejects verwenden.

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

Matcher

.toBe(value)

Verwenden Sie .toBe, um primitive Werte zu vergleichen oder die Referenzidentität von Objektinstanzen zu prüfen. Es verwendet Object.is für den Vergleich, was für Tests besser geeignet ist als der strikte Gleichheitsoperator ===.

Dieser Code validiert beispielsweise einige Eigenschaften des can-Objekts:

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

Verwenden Sie .toBe nicht mit Fließkommazahlen. Aufgrund von Rundungsfehlern ist in JavaScript 0.2 + 0.1 nicht exakt gleich 0.3. Bei Fließkommazahlen verwenden Sie stattdessen .toBeCloseTo.

Obwohl der .toBe-Matcher die referenzielle Identität prüft, zeigt er bei fehlgeschlagenen Assertions einen tiefen Vergleich der Werte an. Wenn Unterschiede in den Eigenschaften nicht helfen zu verstehen, warum ein Test fehlschlägt – insbesondere bei umfangreichen Berichten – können Sie den Vergleich in die expect-Funktion verlagern. Um beispielsweise zu prüfen, ob Elemente dieselbe Instanz sind:

  • Ersetze expect(received).toBe(expected) durch expect(Object.is(received, expected)).toBe(true)

  • Ersetze expect(received).not.toBe(expected) durch expect(Object.is(received, expected)).toBe(false)

.toHaveBeenCalled()

Auch unter dem Alias: .toBeCalled()

Verwende .toHaveBeenCalled, um sicherzustellen, dass eine Mock-Funktion aufgerufen wurde.

Angenommen, du hast eine Funktion drinkAll(drink, flavour), die eine drink-Funktion auf alle verfügbaren Getränke anwendet. Du könntest prüfen wollen, ob drink aufgerufen wird. Das lässt sich mit diesem Test-Setup erreichen:

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)

Auch unter dem Alias: .toBeCalledTimes(number)

Verwende .toHaveBeenCalledTimes, um sicherzustellen, dass eine Mock-Funktion exakt number-mal aufgerufen wurde.

Angenommen, du hast eine Funktion drinkEach(drink, Array<flavor>), die eine drink-Funktion auf ein Array von Getränken anwendet. Du könntest prüfen wollen, ob die drink-Funktion exakt number-mal aufgerufen wurde. Das lässt sich mit diesem Test-Setup erreichen:

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

.toHaveBeenCalledWith(arg1, arg2, ...)

Auch unter dem Alias: .toBeCalledWith()

Verwende .toHaveBeenCalledWith, um sicherzustellen, dass eine Mock-Funktion mit bestimmten Argumenten aufgerufen wurde. Die Argumentprüfung erfolgt mit demselben Algorithmus wie bei .toEqual.

Angenommen, du kannst ein Getränk mit einer register-Funktion registrieren, und applyToAll(f) sollte die Funktion f auf alle registrierten Getränke anwenden. Zur Überprüfung könntest du schreiben:

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, ...)

Auch unter dem Alias: .lastCalledWith(arg1, arg2, ...)

Bei Mock-Funktionen kannst du .toHaveBeenLastCalledWith verwenden, um die Argumente des letzten Aufrufs zu prüfen. Angenommen, du hast eine Funktion applyToAllFlavors(f), die f auf verschiedene Geschmacksrichtungen anwendet, und du möchtest sicherstellen, dass die letzte verarbeitete Geschmacksrichtung 'mango' ist. Du könntest schreiben:

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

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

Auch unter dem Alias: .nthCalledWith(nthCall, arg1, arg2, ...)

Bei Mock-Funktionen kannst du .toHaveBeenNthCalledWith verwenden, um die Argumente des nthCall-ten Aufrufs zu prüfen. Angenommen, du hast eine Funktion drinkEach(drink, Array<flavor>), die f auf mehrere Geschmacksrichtungen anwendet, und du möchtest sicherstellen, dass die erste Geschmacksrichtung 'lemon' und die zweite 'octopus' ist. Du könntest schreiben:

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

Das n-te Argument muss eine positive Ganzzahl ab 1 sein.

.toHaveReturned()

Auch unter dem Alias: .toReturn()

Bei Mock-Funktionen kannst du .toHaveReturned verwenden, um zu prüfen, dass die Funktion mindestens einmal erfolgreich zurückgekehrt ist (d.h. keinen Fehler ausgelöst hat). Angenommen, du hast einen Mock drink, der true zurückgibt. Du könntest schreiben:

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

drink();

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

.toHaveReturnedTimes(number)

Auch unter dem Alias: .toReturnTimes(number)

Verwende .toHaveReturnedTimes, um sicherzustellen, dass eine Mock-Funktion exakt number-mal erfolgreich zurückgekehrt ist (d.h. keinen Fehler ausgelöst hat). Aufrufe, die Fehler auslösen, zählen nicht als erfolgreiche Rückkehr.

Angenommen, du hast einen Mock drink, der true zurückgibt. Du könntest schreiben:

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

drink();
drink();

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

.toHaveReturnedWith(value)

Auch unter dem Alias: .toReturnWith(value)

Verwende .toHaveReturnedWith, um sicherzustellen, dass eine Mock-Funktion einen bestimmten Wert zurückgegeben hat.

Nehmen wir an, du hast eine Mock-Funktion drink, die den Namen des konsumierten Getränks zurückgibt. Du könntest schreiben:

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)

Auch unter dem Alias: .lastReturnedWith(value)

Verwende .toHaveLastReturnedWith, um den spezifischen Rückgabewert des letzten Aufrufs einer Mock-Funktion zu prüfen. Wenn der letzte Aufruf einen Fehler ausgelöst hat, schlägt dieser Matcher unabhängig vom erwarteten Wert fehl.

Nehmen wir an, du hast eine Mock-Funktion drink, die den Namen des konsumierten Getränks zurückgibt. Du könntest schreiben:

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)

Auch unter dem Alias: .nthReturnedWith(nthCall, value)

Verwende .toHaveNthReturnedWith, um den spezifischen Rückgabewert für den n-ten Aufruf einer Mock-Funktion zu prüfen. Wenn der n-te Aufruf einen Fehler ausgelöst hat, schlägt dieser Matcher unabhängig vom erwarteten Wert fehl.

Nehmen wir an, du hast eine Mock-Funktion drink, die den Namen des konsumierten Getränks zurückgibt. Du könntest schreiben:

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)');
});
Hinweis

Das n-te Argument muss eine positive Ganzzahl ab 1 sein.

.toHaveLength(number)

Verwende .toHaveLength, um zu prüfen, dass ein Objekt eine .length-Eigenschaft mit einem bestimmten numerischen Wert besitzt.

Besonders nützlich für die Überprüfung der Größe von Arrays oder Strings.

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

.toHaveProperty(keyPath, value?)

Verwende .toHaveProperty, um die Existenz einer Eigenschaft unter einem bestimmten keyPath zu prüfen. Für tief verschachtelte Eigenschaften kannst du Punktnotation oder ein Array mit dem Pfad verwenden.

Durch den optionalen value-Parameter kannst du den Wert der Eigenschaft vergleichen (rekursiv für alle Objekteigenschaften, tiefe Gleichheit wie bei toEqual).

Das folgende Beispiel zeigt ein houseForSale-Objekt mit verschachtelten Eigenschaften. Wir verwenden toHaveProperty, um Existenz und Werte verschiedener Eigenschaften zu prüfen:

// 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?)

Verwende toBeCloseTo, um Gleitkommazahlen auf annähernde Gleichheit zu prüfen.

Der optionale numDigits-Parameter begrenzt die Anzahl der Nachkommastellen, die überprüft werden. Standardmäßig (2) gilt: Math.abs(expected - received) < 0.005 (d.h. 10 ** -2 / 2).

Vergleiche mit exakter Gleichheit scheitern oft durch Rundungsfehler in der binären Darstellung. Dieser Test schlägt beispielsweise fehl:

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

Grund: In JavaScript ergibt 0.2 + 0.1 tatsächlich 0.30000000000000004.

Beispiel mit 5 Nachkommastellen Genauigkeit:

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

Da toBeCloseTo Rundungsfehler behandelt, unterstützt es keine großen Integer-Werte.

.toBeDefined()

Verwende .toBeDefined, um zu prüfen, dass eine Variable nicht undefined ist. Um zu prüfen, ob fetchNewFlavorIdea() etwas zurückgibt, kannst du schreiben:

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

Alternativ könnte man expect(fetchNewFlavorIdea()).not.toBe(undefined) schreiben, aber der direkte Verweis auf undefined sollte vermieden werden.

.toBeFalsy()

Verwende .toBeFalsy, wenn es dir nicht darauf ankommt, was ein Wert ist und du sicherstellen möchtest, dass ein Wert in einem booleschen Kontext false ist. Beispiel für Anwendungscode:

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

Vielleicht ist dir der genaue Rückgabewert von getErrors egal – es könnte false, null oder 0 sein, dein Code würde trotzdem funktionieren. Wenn du also testen möchtest, dass nach dem Trinken von La Croix keine Fehler auftreten, könntest du schreiben:

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

In JavaScript gibt es sechs falsy-Werte: false, 0, '', null, undefined und NaN. Alles andere ist truthy.

.toBeGreaterThan(number | bigint)

Verwende toBeGreaterThan, um received > expected für Zahlen- oder BigInt-Werte zu prüfen. Beispiel: Teste, dass ouncesPerCan() einen Wert über 10 Unzen zurückgibt:

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

.toBeGreaterThanOrEqual(number | bigint)

Verwende toBeGreaterThanOrEqual, um received >= expected für Zahlen- oder BigInt-Werte zu prüfen. Beispiel: Teste, dass ouncesPerCan() mindestens 12 Unzen zurückgibt:

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

.toBeLessThan(number | bigint)

Verwende toBeLessThan, um received < expected für Zahlen- oder BigInt-Werte zu prüfen. Beispiel: Teste, dass ouncesPerCan() weniger als 20 Unzen zurückgibt:

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

.toBeLessThanOrEqual(number | bigint)

Verwende toBeLessThanOrEqual, um received <= expected für Zahlen- oder BigInt-Werte zu prüfen. Beispiel: Teste, dass ouncesPerCan() höchstens 12 Unzen zurückgibt:

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

.toBeInstanceOf(Class)

Verwende .toBeInstanceOf(Class), um zu prüfen, ob ein Objekt eine Instanz der Klasse ist. Dieser Matcher nutzt intern instanceof.

class A {}

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

.toBeNull()

.toBeNull() ist identisch zu .toBe(null), produziert aber bessere Fehlermeldungen. Verwende .toBeNull(), wenn du prüfen möchtest, ob etwas null ist.

function bloop() {
return null;
}

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

.toBeTruthy()

Verwende .toBeTruthy, wenn der genaue Wert unwichtig ist, du aber sicherstellen möchtest, dass er in einem booleschen Kontext wahr ergibt. Beispiel: Angenommen dein Anwendungscode sieht so aus:

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

Vielleicht ist dir der Rückgabewert von thirstInfo egal – es könnte true oder ein komplexes Objekt sein, dein Code würde trotzdem funktionieren. Um zu testen, dass thirstInfo nach dem Trinken von La Croix truthy ist, könntest du schreiben:

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

In JavaScript gibt es sechs falsy-Werte: false, 0, '', null, undefined und NaN. Alles andere ist truthy.

.toBeUndefined()

Verwende .toBeUndefined, um zu prüfen, ob eine Variable undefined ist. Beispiel: Wenn du testen möchtest, dass bestDrinkForFlavor(flavor) für das Aroma 'octopus' undefined zurückgibt (weil es kein gutes Getränk mit Tintenfischgeschmack gibt):

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

Du könntest expect(bestDrinkForFlavor('octopus')).toBe(undefined) schreiben, aber es ist besser, direkte Referenzen auf undefined im Code zu vermeiden.

.toBeNaN()

Verwende .toBeNaN, um zu prüfen, ob ein Wert NaN ist.

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

.toContain(item)

Verwende .toContain, um zu prüfen, ob ein Element in einem Array existiert. Für Elementprüfungen wird === (strikte Gleichheit) verwendet. .toContain kann auch prüfen, ob ein String Teilstring eines anderen Strings ist.

Beispiel: Wenn getAllFlavors() ein Array von Aromen zurückgibt und du sichergehen möchtest, dass lime enthalten ist:

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

Dieser Matcher akzeptiert auch andere iterierbare Objekte wie Strings, Sets, Node-Listen und HTMLCollections.

.toContainEqual(item)

Verwende .toContainEqual, wenn du überprüfen möchtest, ob ein Element mit spezifischer Struktur und Werten in einem Array enthalten ist. Dieser Matcher prüft rekursiv die Gleichheit aller Felder der Array-Elemente, anstatt die Objektidentität zu vergleichen.

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

.toEqual(value)

Verwende .toEqual, um rekursiv alle Eigenschaften von Objektinstanzen zu vergleichen (auch bekannt als "tiefe" Gleichheit). Es nutzt Object.is zum Vergleich primitiver Werte, was für Tests besser geeignet ist als der strikte Gleichheitsoperator ===.

Beispielsweise verhalten sich .toEqual und .toBe in diesem Test-Set unterschiedlich, sodass alle Tests bestehen:

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);
});
});
Tipp

toEqual ignoriert Objektschlüssel mit undefined-Eigenschaften, undefined-Array-Elemente, Array-Lücken oder Objekttyp-Unterschiede. Um diese zu berücksichtigen, verwende stattdessen .toStrictEqual.

Hinweis

.toEqual führt keinen tiefen Gleichheitsvergleich für zwei Fehlerobjekte durch. Nur die message-Eigenschaft eines Errors wird auf Gleichheit geprüft. Für Fehlertests wird empfohlen, den .toThrow-Matcher zu verwenden.

Wenn Unterschiede zwischen Eigenschaften nicht helfen, einen Testfehler zu verstehen (insbesondere bei umfangreichen Berichten), kannst du den Vergleich in die expect-Funktion verlagern. Verwende beispielsweise die equals-Methode der Buffer-Klasse, um zu prüfen, ob Buffer identische Inhalte haben:

  • Ersetze expect(received).toEqual(expected) durch expect(received.equals(expected)).toBe(true)

  • Ersetze expect(received).not.toEqual(expected) durch expect(received.equals(expected)).toBe(false)

.toMatch(regexp | string)

Verwende .toMatch, um zu prüfen, ob ein String einem regulären Ausdruck entspricht.

Beispiel: Du weißt vielleicht nicht genau, was essayOnTheBestFlavor() zurückgibt, aber du weißt, dass es ein sehr langer String ist und die Teilzeichenkette grapefruit irgendwo enthalten sein sollte. Das kannst du so testen:

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

Dieser Matcher akzeptiert auch einen String, gegen den er vergleicht:

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

.toMatchObject(object)

Verwende .toMatchObject, um zu prüfen, ob ein JavaScript-Objekt einer Teilmenge der Eigenschaften eines anderen Objekts entspricht. Er akzeptiert empfangene Objekte mit Eigenschaften, die nicht im erwarteten Objekt enthalten sind.

Du kannst auch ein Array von Objekten übergeben. In diesem Fall gibt die Methode nur dann true zurück, wenn jedes Objekt im empfangenen Array dem entsprechenden Objekt im erwarteten Array entspricht (gemäß der oben beschriebenen toMatchObject-Logik). Dies ist nützlich, wenn du prüfen möchtest, dass zwei Arrays in ihrer Elementanzahl übereinstimmen, im Gegensatz zu arrayContaining, das zusätzliche Elemente im empfangenen Array erlaubt.

Du kannst Eigenschaften gegen Werte oder andere Matcher prüfen.

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?)

Stellt sicher, dass ein Wert mit dem aktuellsten Snapshot übereinstimmt. Weitere Informationen findest du im Snapshot Testing-Leitfaden.

Du kannst optional ein propertyMatchers-Objekt als Argument übergeben, das asymmetrische Matcher als Werte für eine Teilmenge der erwarteten Eigenschaften enthält, falls der empfangene Wert eine Objekt-Instanz ist. Das funktioniert ähnlich wie toMatchObject mit flexiblen Kriterien für einen Teil der Eigenschaften, gefolgt von einem Snapshot-Test als exakten Kriterien für die restlichen Eigenschaften.

Du kannst optional einen hint-String angeben, der an den Testnamen angehängt wird. Obwohl Jest immer eine Zahl an den Snapshot-Namen anhängt, können kurze beschreibende Hinweise nützlicher sein als Zahlen, um mehrere Snapshots in einem einzigen it- oder test-Block zu unterscheiden. Jest sortiert Snapshots im entsprechenden .snap-File nach Namen.

.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)

Stellt sicher, dass ein Wert mit dem aktuellsten Snapshot übereinstimmt.

Du kannst optional ein propertyMatchers-Objekt als Argument übergeben, das asymmetrische Matcher als Werte für eine Teilmenge der erwarteten Eigenschaften enthält, falls der empfangene Wert eine Objekt-Instanz ist. Das funktioniert ähnlich wie toMatchObject mit flexiblen Kriterien für einen Teil der Eigenschaften, gefolgt von einem Snapshot-Test als exakten Kriterien für die restlichen Eigenschaften.

Jest fügt beim ersten Ausführen des Tests das inlineSnapshot-String-Argument direkt in die Testdatei ein (anstatt in eine externe .snap-Datei).

Weitere Informationen findest du im Abschnitt Inline-Snapshots.

.toStrictEqual(value)

Verwende .toStrictEqual, um zu testen, dass Objekte dieselbe Struktur und denselben Typ haben.

Unterschiede zu .toEqual:

  • Eigenschaften mit undefined-Werten werden geprüft, z.B. {a: undefined, b: 2} ist nicht gleich {b: 2};

  • undefined-Elemente werden berücksichtigt, z.B. [2] ist nicht gleich [2, undefined];

  • Die Sparsity von Arrays wird geprüft, z.B. [, 1] ist nicht gleich [undefined, 1];

  • Objekttypen werden geprüft, z.B. eine Klasseninstanz mit Feldern a und b ist nicht gleich einem Literal-Objekt mit Feldern a und 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?)

Auch unter dem Alias: .toThrowError(error?)

Verwende .toThrow, um zu testen, dass eine Funktion beim Aufruf einen Fehler auslöst. Wenn wir beispielsweise testen möchten, dass drinkFlavor('octopus') einen Fehler auslöst, weil Octopus-Geschmack zu eklig ist, könnten wir schreiben:

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

Du musst den Code in eine Funktion wrappen, sonst wird der Fehler nicht abgefangen und die Assertion schlägt fehl.

Du kannst ein optionales Argument angeben, um einen spezifischen Fehler zu testen:

  • Regulärer Ausdruck: Fehlermeldung stimmt mit dem Muster überein

  • String: Fehlermeldung enthält die Teilzeichenkette

  • Fehlerobjekt: Fehlermeldung ist identisch mit der message-Eigenschaft des Objekts

  • Fehlerklasse: Fehlerobjekt ist Instanz von der Klasse

Angenommen, drinkFlavor ist wie folgt implementiert:

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

Wir könnten diesen Fehler auf verschiedene Arten testen:

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?)

Verwende .toThrowErrorMatchingSnapshot, um zu testen, dass eine Funktion einen Fehler auslöst, der mit dem aktuellsten Snapshot übereinstimmt.

Du kannst optional einen hint-String angeben, der an den Testnamen angehängt wird. Obwohl Jest immer eine Zahl an den Snapshot-Namen anhängt, können kurze beschreibende Hinweise nützlicher sein als Zahlen, um mehrere Snapshots in einem einzigen it- oder test-Block zu unterscheiden. Jest sortiert Snapshots im entsprechenden .snap-File nach Namen.

Angenommen, du hast eine drinkFlavor-Funktion, die bei Geschmack 'octopus' einen Fehler auslöst:

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

Der Test für diese Funktion würde so aussehen:

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

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

Und er generiert folgenden Snapshot:

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

Weitere Informationen zum Snapshot-Testing findest du im Blogpost React Tree Snapshot Testing.

.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)

Verwende .toThrowErrorMatchingInlineSnapshot, um zu testen, dass eine Funktion einen Fehler auslöst, der mit dem aktuellsten Snapshot übereinstimmt.

Jest fügt beim ersten Ausführen des Tests das inlineSnapshot-String-Argument direkt in die Testdatei ein (anstatt in eine externe .snap-Datei).

Weitere Informationen findest du im Abschnitt Inline-Snapshots.

Asymmetrische Matcher

expect.anything()

expect.anything() passt auf alles außer null oder undefined. Du kannst es in toEqual oder toHaveBeenCalledWith anstelle eines Literalwerts verwenden. Beispiel, um zu prüfen, ob eine Mock-Funktion mit einem nicht-null Argument aufgerufen wurde:

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) passt auf alles, was mit dem gegebenen Konstruktor erstellt wurde oder einen primitiven Typ des übergebenen Typs hat. Du kannst es innerhalb von toEqual oder toHaveBeenCalledWith anstelle eines Literalwerts verwenden. Beispiel, um zu prüfen, ob eine Mock-Funktion mit einer Zahl aufgerufen wurde:

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) passt auf ein empfangenes Array, das alle Elemente des erwarteten Arrays enthält. Das erwartete Array ist also eine Teilmenge des empfangenen Arrays. Es passt auch, wenn das empfangene Array zusätzliche Elemente enthält.

Sie können dies anstelle eines Literalwerts verwenden:

  • in toEqual oder toHaveBeenCalledWith

  • um eine Eigenschaft in objectContaining oder toMatchObject abzugleichen

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) passt auf ein empfangenes Array, das nicht alle Elemente des erwarteten Arrays enthält. Das erwartete Array ist keine Teilmenge des empfangenen Arrays.

Dies ist das Gegenstück zu 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.closeTo(number, numDigits?)

expect.closeTo(number, numDigits?) eignet sich zum Vergleichen von Fließkommazahlen in Objekteigenschaften oder Array-Elementen. Für einfache Zahlenvergleiche verwende stattdessen .toBeCloseTo.

Der optionale numDigits-Parameter begrenzt die Nachkommastellen. Standardwert 2 bedeutet: Math.abs(expected - received) < 0.005 (that is, 10 ** -2 / 2).

Beispiel mit 5 Nachkommastellen Genauigkeit:

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) passt auf jedes empfangene Objekt, das rekursiv die erwarteten Eigenschaften enthält. Das erwartete Objekt ist eine Teilmenge des empfangenen Objekts. Zusätzliche Eigenschaften sind erlaubt.

Statt Literalwerten kannst du Matcher wie expect.anything() verwenden.

Beispiel: Prüfen, ob eine onPress-Funktion mit einem Event-Objekt aufgerufen wurde, und dass das Event die Eigenschaften event.x und event.y hat. Wir können das so machen:

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) passt auf empfangene Objekte, die nicht die erwarteten Eigenschaften enthalten. Das erwartete Objekt ist keine Teilmenge des empfangenen Objekts.

Es ist das Gegenstück zu 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) trifft zu, wenn der empfangene Wert ein String ist, der den exakt erwarteten String enthält.

expect.not.stringContaining(string)

expect.not.stringContaining(string) trifft zu, wenn der empfangene Wert kein String ist oder wenn es ein String ist, der den exakt erwarteten String nicht enthält.

Es ist das Gegenstück zu 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) trifft zu, wenn der empfangene Wert ein String ist, der mit dem erwarteten String oder regulären Ausdruck übereinstimmt.

Sie können dies anstelle eines Literalwerts verwenden:

  • in toEqual oder toHaveBeenCalledWith

  • um ein Element in arrayContaining abzugleichen

  • um eine Eigenschaft in objectContaining oder toMatchObject abzugleichen

Dieses Beispiel zeigt auch, wie Sie mehrere asymmetrische Matcher verschachteln können, hier expect.stringMatching innerhalb von 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) trifft zu, wenn der empfangene Wert kein String ist oder wenn es ein String ist, der nicht mit dem erwarteten String oder regulären Ausdruck übereinstimmt.

Es ist das Gegenstück zu 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));
});
});

Assertion-Zählung

expect.assertions(number)

expect.assertions(number) stellt sicher, dass eine bestimmte Anzahl von Assertions während eines Tests aufgerufen wird. Dies ist besonders nützlich beim Testen von asynchronem Code, um sicherzustellen, dass Assertions in Callbacks tatsächlich ausgeführt werden.

Angenommen, wir haben eine Funktion doAsync, die zwei Callbacks callback1 und callback2 erhält und diese asynchron in unbekannter Reihenfolge aufruft. Wir können dies wie folgt testen:

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

doAsync(callback1, callback2);
});

Der Aufruf expect.assertions(2) stellt sicher, dass beide Callbacks tatsächlich aufgerufen werden.

expect.hasAssertions()

expect.hasAssertions() stellt sicher, dass mindestens eine Assertion während eines Tests aufgerufen wird. Dies ist besonders nützlich beim Testen von asynchronem Code, um sicherzustellen, dass Assertions in Callbacks tatsächlich ausgeführt werden.

Angenommen, wir haben mehrere Funktionen, die mit Zuständen arbeiten. prepareState ruft einen Callback mit einem Zustandsobjekt auf, validateState verarbeitet dieses Zustandsobjekt, und waitOnState gibt ein Promise zurück, das wartet, bis alle prepareState-Callbacks abgeschlossen sind. Wir können dies wie folgt testen:

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

Der Aufruf expect.hasAssertions() stellt sicher, dass der prepareState-Callback tatsächlich aufgerufen wird.

Erweiterungs-Hilfsmittel

expect.addEqualityTesters(testers)

Mit expect.addEqualityTesters können Sie eigene Methoden hinzufügen, um zu prüfen, ob zwei Objekte gleich sind. Angenommen, Sie haben eine Klasse in Ihrem Code, die Volumen darstellt und bestimmen kann, ob zwei Volumen mit verschiedenen Einheiten gleich sind. Sie möchten, dass toEqual (und andere Gleichheits-Matcher) diese benutzerdefinierte Gleichheitsmethode beim Vergleich von Volumen-Klassen verwendet. Sie können einen benutzerdefinierten Equality Tester hinzufügen, damit toEqual benutzerdefinierte Logik beim Vergleich von Volumen-Klassen erkennt und anwendet:

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 für benutzerdefinierte Equality Tester

Benutzerdefinierte Tester sind Funktionen, die entweder das Ergebnis (true oder false) des Vergleichs der beiden gegebenen Argumente zurückgeben oder undefined, wenn der Tester die gegebenen Objekte nicht verarbeitet und die Gleichheitsprüfung anderen Testern überlassen möchte (z.B. den integrierten Equality Testern).

Benutzerdefinierte Tester werden mit 3 Argumenten aufgerufen: den beiden zu vergleichenden Objekten und dem Array der benutzerdefinierten Tester (wird für rekursive Tester verwendet, siehe folgenden Abschnitt).

Diese Hilfsfunktionen und Eigenschaften sind innerhalb eines benutzerdefinierten Testers über this verfügbar:

this.equals(a, b, customTesters?)

Diese Deep-Equality-Funktion gibt true zurück, wenn zwei Objekte denselben Wert haben (rekursiv). Optional kann eine Liste benutzerdefinierter Equality-Tester für die tiefen Gleichheitsprüfungen übergeben werden. Wenn Sie diese Funktion verwenden, reichen Sie die erhaltenen Custom Testers weiter, damit nachfolgende equals-Prüfungen ebenfalls benutzerdefinierte Tester nutzen können. Weitere Details finden Sie im Beispiel im Abschnitt Rekursive benutzerdefinierte Equality-Tester.

Matcher vs. Tester

Matcher sind Methoden an expect, wie z.B. expect().toEqual(). toEqual ist ein Matcher. Ein Tester ist eine Methode, die von Matchern für Gleichheitsprüfungen verwendet wird, um festzustellen, ob Objekte identisch sind.

Benutzerdefinierte Matcher eignen sich, wenn Sie eine eigene Assertion bereitstellen möchten, die Testautoren verwenden können. Das toBeWithinRange-Beispiel im Abschnitt expect.extend ist ein gutes Beispiel für einen benutzerdefinierten Matcher. Manchmal möchten Testautoren exakte Gleichheit prüfen (dann ist toBe geeignet). In anderen Fällen kann Flexibilität erwünscht sein – dann wäre toBeWithinRange passender.

Benutzerdefinierte Equality-Tester eignen sich für globale Erweiterungen von Jest-Matchern, um eigene Gleichheitslogik für alle Vergleiche zu definieren. Testautoren können Custom Tester nicht selektiv aktivieren/deaktivieren (dafür wäre ein Custom Matcher nötig). Beispiel: Die Definition einer Prüfung für Volume-Objekte wäre ein guter Anwendungsfall für einen Equality-Tester.

Rekursive benutzerdefinierte Equality-Tester

Wenn Ihre Custom Equality Tester Objekte mit Eigenschaften prüfen, die tiefe Gleichheit erfordern, verwenden Sie die in Testern verfügbare Hilfsfunktion this.equals. Diese equals-Methode entspricht Jests interner Deep-Equals-Logik und ruft Ihre benutzerdefinierten Tester auf. Sie akzeptiert als drittes Argument ein Array von Custom Equality Testers. Reichen Sie dieses Argument in equals weiter, damit tiefere Objektebenen ebenfalls benutzerdefinierte Tester nutzen können.

Angenommen, Sie haben eine Book-Klasse mit einem Array von Author-Klassen, die beide benutzerdefinierte Tester verwenden. Der Book-Tester würde eine tiefe Gleichheitsprüfung für das Authors-Array durchführen und dabei die erhaltenen Custom Tester weiterreichen, damit der benutzerdefinierte Gleichheitstester für Authors angewendet wird:

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]);
Hinweis

Definieren Sie Equality-Tester als reguläre Funktionen (nicht als Pfeilfunktionen), um auf Kontext-Hilfsmethoden wie this.equals zugreifen zu können.

expect.addSnapshotSerializer(serializer)

Mit expect.addSnapshotSerializer können Sie ein Modul hinzufügen, das anwendungsspezifische Datenstrukturen formatiert.

Für eine einzelne Testdatei hat ein hinzugefügtes Modul Vorrang vor Modulen aus der snapshotSerializers-Konfiguration. Diese haben wiederum Vorrang vor Standardserialisierern für JavaScript-Typen und React-Elemente. Das zuletzt hinzugefügte Modul wird zuerst getestet.

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

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

Wenn Sie einen Snapshot-Serializer in einzelnen Testdateien hinzufügen statt in der snapshotSerializers-Konfiguration:

  • Machen Sie Abhängigkeiten explizit statt implizit

  • Vermeiden Sie Konfigurationsbeschränkungen, die zum Eject aus create-react-app zwingen könnten

Weitere Informationen finden Sie unter Jest konfigurieren.

expect.extend(matchers)

Mit expect.extend können Sie eigene Matcher zu Jest hinzufügen. Angenommen, Sie testen eine Zahlenbibliothek und prüfen häufig, ob Zahlen in bestimmten Bereichen liegen. Diese Logik könnten Sie in einen toBeWithinRange-Matcher abstrahieren:

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 {};
Tipp

Die Typdeklaration des Matchers kann in einer .d.ts-Datei oder in einem importierten .ts-Modul leben (siehe JS- und TS-Beispiele oben). Wenn Sie die Deklaration in einer .d.ts-Datei belassen, stellen Sie sicher, dass sie im Programm enthalten ist und ein gültiges Modul darstellt, d.h. mindestens einen leeren export {} enthält.

Tipp

Statt das toBeWithinRange-Modul in jede Testdatei zu importieren, können Sie den Matcher für alle Tests aktivieren, indem Sie den expect.extend-Aufruf in ein setupFilesAfterEnv-Skript verschieben:

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

expect.extend({
toBeWithinRange,
});

Asynchrone Matchers

expect.extend unterstützt auch asynchrone Matcher. Asynchrone Matcher geben ein Promise zurück, daher müssen Sie den Rückgabewert erwarten (await). Ein Beispielmatcher toBeDivisibleByExternalValue verdeutlicht die Verwendung. Hier wird die teilbare Zahl aus einer externen Quelle bezogen:

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 für benutzerdefinierte Matchers

Matcher sollten ein Objekt (oder Promise eines Objekts) mit zwei Schlüsseln zurückgeben: pass zeigt an, ob eine Übereinstimmung vorliegt, und message liefert eine Funktion ohne Argumente, die eine Fehlermeldung bei Misserfolg zurückgibt. Ist pass false, muss message die Fehlermeldung für expect(x).yourMatcher() liefern. Ist pass true, muss message die Fehlermeldung für expect(x).not.yourMatcher() liefern.

Matcher werden mit dem an expect(x) übergebenen Argument aufgerufen, gefolgt von den an .yourMatcher(y, z) übergebenen Argumenten:

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

Diese Hilfsfunktionen und Eigenschaften sind innerhalb eines benutzerdefinierten Matchers über this verfügbar:

this.isNot

Ein Boolean, der anzeigt, ob der Matcher mit dem .not-Modifikator aufgerufen wurde. Ermöglicht präzise Matcher-Hinweise (siehe Beispielcode).

this.promise

Ein String zur Anzeige präziser Matcher-Hinweise:

  • 'rejects' bei Aufruf mit Promise-.rejects-Modifikator

  • 'resolves' bei Aufruf mit Promise-.resolves-Modifikator

  • '' ohne Promise-Modifikator

this.equals(a, b, customTesters?)

Eine Deep-Equality-Funktion, die true zurückgibt, wenn zwei Objekte identische Werte haben (rekursiv). Optional können benutzerdefinierte Equality-Tester übergeben werden (siehe this.customTesters).

this.expand

Ein Boolean, der anzeigt, ob der Matcher mit der expand-Option aufgerufen wurde. Bei Verwendung der --expand-Flag in Jest steuert this.expand, ob vollständige Diffs und Fehler angezeigt werden.

this.utils

Hilfreiche Tools von jest-matcher-utils sind über this.utils verfügbar.

Besonders nützlich sind matcherHint, printExpected und printReceived zur ansprechenden Fehlermeldungsformatierung. Beispielimplementierung des toBe-Matchers:

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};
},
});

Dies erzeugt Ausgaben wie:

  expect(received).toBe(expected)

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

Fehlermeldungen sollten präzise Signale liefern, damit Nutzer Probleme schnell lösen können. Gestalten Sie aussagekräftige Meldungen für eine gute Developer Experience.

this.customTesters

Falls dein Matcher eine tiefe Gleichheitsprüfung mit this.equals durchführt, möchtest du möglicherweise benutzerdefinierte Tester an this.equals übergeben. Die benutzerdefinierten Gleichheitstester, die der Benutzer über die addEqualityTesters-API bereitgestellt hat, sind in dieser Eigenschaft verfügbar. Die integrierten Jest-Matcher übergeben this.customTesters (zusammen mit anderen integrierten Testern) an this.equals, um tiefe Gleichheit zu prüfen, und deine benutzerdefinierten Matcher sollten möglicherweise dasselbe tun.

Benutzerdefinierte Snapshot-Matcher

Um Snapshot-Tests in deinem benutzerdefinierten Matcher zu verwenden, kannst du jest-snapshot importieren und innerhalb deines Matchers verwenden.

Hier ist ein Snapshot-Matcher, der einen String auf eine bestimmte Länge kürzt, .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"`;
*/

Es ist auch möglich, benutzerdefinierte Matcher für Inline-Snapshots zu erstellen. Die Snapshots werden korrekt zu den benutzerdefinierten Matchern hinzugefügt. Inline-Snapshots versuchen jedoch immer, das erste Argument anzuhängen oder das zweite, wenn das erste Argument der Eigenschaften-Matcher ist. Daher ist es nicht möglich, benutzerdefinierte Argumente in benutzerdefinierten Matchern zu akzeptieren.

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"`
);
*/
});

Asynchron

Wenn dein benutzerdefinierter Inline-Snapshot-Matcher asynchron ist (d.h. async-await verwendet), könntest du auf einen Fehler wie "Mehrere Inline-Snapshots für denselben Aufruf werden nicht unterstützt" stoßen. Jest benötigt zusätzliche Kontextinformationen, um zu ermitteln, wo der benutzerdefinierte Inline-Snapshot-Matcher verwendet wurde, um die Snapshots korrekt zu aktualisieren.

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"`);
*/
});

Abbruch

Normalerweise versucht jest, jeden Snapshot abzugleichen, der in einem Test erwartet wird.

Manchmal ist es nicht sinnvoll, den Test fortzusetzen, wenn ein vorheriger Snapshot fehlgeschlagen ist. Zum Beispiel, wenn du Snapshots einer Zustandsmaschine nach verschiedenen Übergängen erstellst, kannst du den Test abbrechen, sobald ein Übergang einen falschen Zustand erzeugt hat.

In diesem Fall kannst du einen benutzerdefinierten Snapshot-Matcher implementieren, der beim ersten Fehlschlag eine Ausnahme auslöst, anstatt jeden Fehlschlag zu sammeln.

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"`);
});