Vai al contenuto principale
{ "message": "Versione: 29.7", "description": "" }

Expect

Traduzione Beta Non Ufficiale

Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →

Durante la scrittura dei test, spesso è necessario verificare che i valori soddisfino determinate condizioni. expect fornisce accesso a una serie di "matcher" che ti permettono di convalidare diversi aspetti.

suggerimento

Per ulteriori matcher Jest mantenuti dalla community, consulta jest-extended.

Traduzione Beta Non Ufficiale

Questa pagina è stata tradotta da PageTurner AI (beta). Non ufficialmente approvata dal progetto. Hai trovato un errore? Segnala problema →

info

Gli esempi TypeScript in questa pagina funzioneranno come documentato solo se importi esplicitamente le API di Jest:

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

Consulta la guida Per iniziare per i dettagli su come configurare Jest con TypeScript.

Riferimento


La funzione expect

expect(value)

La funzione expect viene utilizzata ogni volta che vuoi testare un valore. Raramente chiamerai expect da sola. Piuttosto, userai expect insieme a una funzione "matcher" per verificare qualcosa riguardo a un valore.

È più facile capire con un esempio. Supponi di avere un metodo bestLaCroixFlavor() che dovrebbe restituire la stringa 'grapefruit'. Ecco come lo testeresti:

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

In questo caso, toBe è la funzione matcher. Esistono molte funzioni matcher diverse, documentate di seguito, per aiutarti a testare vari aspetti.

L'argomento di expect dovrebbe essere il valore prodotto dal tuo codice, mentre l'argomento del matcher dovrebbe essere il valore corretto. Se li inverti, i test funzioneranno comunque, ma i messaggi di errore appariranno confusi.

Modificatori

.not

Se sai come testare qualcosa, .not ti permette di testare il suo opposto. Ad esempio, questo codice verifica che il miglior gusto La Croix non sia cocco:

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

.resolves

Usa resolves per estrarre il valore di una promise risolta, così puoi concatenare altri matcher. Se la promise viene rifiutata, l'asserzione fallisce.

Ad esempio, questo codice verifica che la promise venga risolta e che il valore risultante sia 'lemon':

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

Poiché stai ancora testando promise, il test rimane asincrono. Dovrai quindi dire a Jest di attendere restituendo l'asserzione estratta.

In alternativa, puoi usare async/await in combinazione con .resolves:

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

.rejects

Usa .rejects per estrarre il motivo di una promise rifiutata, così puoi concatenare altri matcher. Se la promise viene risolta, l'asserzione fallisce.

Ad esempio, questo codice verifica che la promise venga rifiutata con il motivo 'octopus':

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

Poiché stai ancora testando promise, il test rimane asincrono. Dovrai quindi dire a Jest di attendere restituendo l'asserzione estratta.

In alternativa, puoi usare async/await in combinazione con .rejects.

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

Matcher

.toBe(value)

Usa .toBe per confrontare valori primitivi o verificare l'identità referenziale di istanze di oggetti. Utilizza Object.is per confrontare i valori, che è persino migliore per i test dell'operatore di uguaglianza stretta ===.

Ad esempio, questo codice convalida alcune proprietà dell'oggetto can:

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

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

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

Non usare .toBe con numeri a virgola mobile. Ad esempio, a causa dell'arrotondamento in JavaScript, 0.2 + 0.1 non è strettamente uguale a 0.3. Per i numeri a virgola mobile, usa invece .toBeCloseTo.

Anche se il matcher .toBe verifica l'identità referenziale, segnala un confronto approfondito dei valori se l'asserzione fallisce. Se le differenze tra le proprietà non ti aiutano a capire perché un test fallisce, specialmente se il report è lungo, puoi spostare il confronto direttamente nella funzione expect. Ad esempio, per verificare se gli elementi sono la stessa istanza:

  • riscrivi expect(received).toBe(expected) come expect(Object.is(received, expected)).toBe(true)

  • riscrivi expect(received).not.toBe(expected) come expect(Object.is(received, expected)).toBe(false)

.toHaveBeenCalled()

Anche con l'alias: .toBeCalled()

Usa .toHaveBeenCalled per verificare che una funzione mock sia stata chiamata.

Ad esempio, supponi di avere una funzione drinkAll(drink, flavour) che applica una funzione drink a tutte le bevande disponibili. Potresti voler verificare che drink venga effettivamente chiamata. Puoi farlo con questo test:

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

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

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

.toHaveBeenCalledTimes(number)

Anche con l'alias: .toBeCalledTimes(number)

Usa .toHaveBeenCalledTimes per verificare che una funzione mock sia stata chiamata un numero preciso di volte.

Ad esempio, supponi di avere una funzione drinkEach(drink, Array<flavor>) che applica una funzione drink a un array di bevande. Potresti voler verificare che la funzione drink sia stata chiamata esattamente il numero corretto di volte. Puoi farlo con questo test:

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

.toHaveBeenCalledWith(arg1, arg2, ...)

Anche con l'alias: .toBeCalledWith()

Usa .toHaveBeenCalledWith per verificare che una funzione mock sia stata chiamata con argomenti specifici. Gli argomenti vengono confrontati con lo stesso algoritmo usato da .toEqual.

Ad esempio, supponi di poter registrare una bevanda con una funzione register, e che applyToAll(f) debba applicare la funzione f a tutte le bevande registrate. Per verificare che funzioni, potresti scrivere:

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

Anche con l'alias: .lastCalledWith(arg1, arg2, ...)

Se hai una funzione mock, puoi usare .toHaveBeenLastCalledWith per verificare con quali argomenti è stata chiamata per ultima. Ad esempio, supponi di avere una funzione applyToAllFlavors(f) che applica f a diversi gusti, e vuoi assicurarti che quando viene chiamata, l'ultimo gusto elaborato sia 'mango'. Puoi scrivere:

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

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

Anche con l'alias: .nthCalledWith(nthCall, arg1, arg2, ...)

Se hai una funzione mock, puoi usare .toHaveBeenNthCalledWith per verificare con quali argomenti è stata chiamata all'n-esima chiamata. Ad esempio, supponi di avere una funzione drinkEach(drink, Array<flavor>) che applica f a diversi gusti, e vuoi assicurarti che quando viene chiamata, il primo gusto elaborato sia 'lemon' e il secondo 'octopus'. Puoi scrivere:

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

L'argomento n deve essere un numero intero positivo a partire da 1.

.toHaveReturned()

Anche con l'alias: .toReturn()

Se hai una funzione mock, puoi usare .toHaveReturned per verificare che la funzione mock sia tornata con successo (cioè senza lanciare errori) almeno una volta. Ad esempio, supponi di avere un mock drink che restituisce true. Puoi scrivere:

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

drink();

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

.toHaveReturnedTimes(number)

Anche con l'alias: .toReturnTimes(number)

Usa .toHaveReturnedTimes per verificare che una funzione mock sia tornata con successo (senza lanciare errori) un numero preciso di volte. Le chiamate che generano errori non vengono conteggiate.

Ad esempio, supponi di avere un mock drink che restituisce true. Puoi scrivere:

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

drink();
drink();

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

.toHaveReturnedWith(value)

Anche con l'alias: .toReturnWith(value)

Usa .toHaveReturnedWith per verificare che una funzione mock abbia restituito un valore specifico.

Ad esempio, supponi di avere una funzione mock drink che restituisce il nome della bevanda consumata. Puoi scrivere:

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)

Anche con l'alias: .lastReturnedWith(value)

Usa .toHaveLastReturnedWith per verificare il valore specifico restituito dall'ultima chiamata a una funzione mock. Se l'ultima chiamata ha generato un errore, questo matcher fallirà indipendentemente dal valore fornito come risultato atteso.

Ad esempio, supponi di avere una funzione mock drink che restituisce il nome della bevanda consumata. Puoi scrivere:

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)

Anche con l'alias: .nthReturnedWith(nthCall, value)

Usa .toHaveNthReturnedWith per verificare il valore specifico restituito da una funzione mock nella chiamata numero n. Se l'n-esima chiamata ha generato un errore, questo matcher fallirà indipendentemente dal valore fornito come risultato atteso.

Ad esempio, supponi di avere una funzione mock drink che restituisce il nome della bevanda consumata. Puoi scrivere:

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

L'argomento n deve essere un numero intero positivo a partire da 1.

.toHaveLength(number)

Usa .toHaveLength per verificare che un oggetto abbia una proprietà .length con un valore numerico specifico.

Particolarmente utile per controllare la dimensione di array o stringhe.

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

.toHaveProperty(keyPath, value?)

Usa .toHaveProperty per verificare l'esistenza di una proprietà nel percorso keyPath di un oggetto. Per proprietà annidate, utilizza la dot notation o un array contenente il percorso completo.

Puoi fornire un argomento opzionale value per confrontare il valore della proprietà (con uguaglianza profonda come il matcher toEqual).

L'esempio seguente mostra un oggetto houseForSale con proprietà annidate. Usiamo toHaveProperty per verificare esistenza e valori di diverse proprietà.

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

Usa toBeCloseTo per confrontare numeri in virgola mobile con uguaglianza approssimata.

L'argomento opzionale numDigits limita il numero di cifre decimali da verificare. Con il valore predefinito 2, il criterio è Math.abs(expected - received) < 0.005 (cioè 10 ** -2 / 2).

I confronti diretti spesso falliscono per errori di arrotondamento nella rappresentazione binaria. Ad esempio, questo test fallisce:

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

Fallisce perché in JavaScript 0.2 + 0.1 restituisce effettivamente 0.30000000000000004.

Esempio con precisione a 5 cifre decimali:

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

Poiché toBeCloseTo risolve specificamente errori da virgola mobile, non supporta valori interi molto grandi.

.toBeDefined()

Usa .toBeDefined per verificare che una variabile non sia undefined. Ad esempio, per controllare che una funzione fetchNewFlavorIdea() restituisca qualcosa:

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

Potresti scrivere expect(fetchNewFlavorIdea()).not.toBe(undefined), ma è buona pratica evitare di riferirsi direttamente a undefined.

.toBeFalsy()

Usa .toBeFalsy quando vuoi verificare che un valore sia falso in un contesto booleano, indipendentemente dal suo tipo. Ad esempio, supponi questo codice:

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

Potresti non interessarti di cosa restituisce getErrors nello specifico - potrebbe essere false, null o 0, e il tuo codice funzionerebbe comunque. Quindi, se vuoi verificare che non ci siano errori dopo aver bevuto una La Croix, potresti scrivere:

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

In JavaScript esistono sei valori falsy: false, 0, '', null, undefined e NaN. Tutto il resto è considerato truthy.

.toBeGreaterThan(number | bigint)

Usa toBeGreaterThan per verificare received > expected con valori numerici o big integer. Ad esempio, per testare che ouncesPerCan() restituisca un valore maggiore di 10 once:

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

.toBeGreaterThanOrEqual(number | bigint)

Usa toBeGreaterThanOrEqual per verificare received >= expected con valori numerici o big integer. Ad esempio, per testare che ouncesPerCan() restituisca almeno 12 once:

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

.toBeLessThan(number | bigint)

Usa toBeLessThan per verificare received < expected con valori numerici o big integer. Ad esempio, per testare che ouncesPerCan() restituisca meno di 20 once:

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

.toBeLessThanOrEqual(number | bigint)

Usa toBeLessThanOrEqual per verificare received <= expected con valori numerici o big integer. Ad esempio, per testare che ouncesPerCan() restituisca al massimo 12 once:

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

.toBeInstanceOf(Class)

Usa .toBeInstanceOf(Class) per verificare che un oggetto sia istanza di una classe. Questo matcher utilizza internamente instanceof.

class A {}

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

.toBeNull()

.toBeNull() è equivalente a .toBe(null) ma produce messaggi d'errore più chiari. Usa .toBeNull() quando vuoi verificare esplicitamente un valore null.

function bloop() {
return null;
}

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

.toBeTruthy()

Usa .toBeTruthy quando non ti interessa il valore specifico ma vuoi assicurarti che sia truthy in un contesto booleano. Ad esempio, supponi di avere del codice come:

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

Potresti non interessarti di cosa restituisce thirstInfo - potrebbe essere true o un oggetto complesso - purché il codice funzioni. Per verificare che thirstInfo sia truthy dopo aver bevuto una La Croix:

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

In JavaScript esistono sei valori falsy: false, 0, '', null, undefined e NaN. Tutto il resto è considerato truthy.

.toBeUndefined()

Usa .toBeUndefined per verificare che una variabile sia undefined. Ad esempio, per testare che bestDrinkForFlavor(flavor) restituisca undefined per il gusto 'octopus' (non esistendo bevande al gusto di polpo):

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

Potresti usare expect(bestDrinkForFlavor('octopus')).toBe(undefined), ma è buona pratica evitare riferimenti diretti a undefined.

.toBeNaN()

Usa .toBeNaN per verificare che un valore sia NaN.

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

.toContain(item)

Usa .toContain quando vuoi verificare che un elemento sia presente in un array. Per testare gli elementi nell'array, questo utilizza ===, un controllo di uguaglianza stretta. .toContain può anche verificare se una stringa è una sottostringa di un'altra stringa.

Ad esempio, se getAllFlavors() restituisce un array di gusti e vuoi assicurarti che contenga lime:

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

Questo matcher supporta anche altri iterabili come stringhe, set, node list e HTML collections.

.toContainEqual(item)

Usa .toContainEqual quando vuoi verificare che un elemento con una struttura e valori specifici sia contenuto in un array. Per testare gli elementi nell'array, questo matcher controlla ricorsivamente l'uguaglianza di tutti i campi, invece di verificare l'identità dell'oggetto.

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

.toEqual(value)

Usa .toEqual per confrontare ricorsivamente tutte le proprietà delle istanze degli oggetti (noto anche come uguaglianza "deep"). Chiama Object.is per confrontare valori primitivi, che è ancora più efficace per i test dell'operatore di uguaglianza stretta ===.

Ad esempio, .toEqual e .toBe si comportano diversamente in questa suite di test, quindi tutti i test passano:

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

toEqual ignora le chiavi degli oggetti con proprietà undefined, elementi di array undefined, sparsità degli array o mancata corrispondenza del tipo di oggetto. Per tenerne conto usa invece .toStrictEqual.

info

.toEqual non eseguirà un controllo di uguaglianza deep per due errori. Solo la proprietà message di un Error è considerata per l'uguaglianza. Si consiglia di usare il matcher .toThrow per i test sugli errori.

Se le differenze tra le proprietà non ti aiutano a capire perché un test fallisce, specialmente se il report è lungo, puoi spostare il confronto nella funzione expect. Ad esempio, usa il metodo equals della classe Buffer per verificare se i buffer contengono lo stesso contenuto:

  • riscrivi expect(received).toEqual(expected) come expect(received.equals(expected)).toBe(true)

  • riscrivi expect(received).not.toEqual(expected) come expect(received.equals(expected)).toBe(false)

.toMatch(regexp | string)

Usa .toMatch per verificare che una stringa corrisponda a un'espressione regolare.

Ad esempio, potresti non sapere cosa restituisca esattamente essayOnTheBestFlavor(), ma sai che è una stringa molto lunga e che la sottostringa grapefruit dovrebbe essere presente da qualche parte. Puoi testarlo con:

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

Questo matcher accetta anche una stringa, che proverà a confrontare:

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

.toMatchObject(object)

Usa .toMatchObject per verificare che un oggetto JavaScript corrisponda a un sottoinsieme delle proprietà di un altro oggetto. Confronterà gli oggetti ricevuti con proprietà che non sono nell'oggetto atteso.

Puoi anche passare un array di oggetti, in tal caso il metodo restituirà true solo se ogni oggetto nell'array ricevuto corrisponde (nel senso di toMatchObject descritto sopra) all'oggetto corrispondente nell'array atteso. È utile se vuoi verificare che due array corrispondano nel numero di elementi, a differenza di arrayContaining che permette elementi extra nell'array ricevuto.

Puoi confrontare proprietà con valori o con altri matcher.

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

Questo garantisce che un valore corrisponda allo snapshot più recente. Consulta la guida allo Snapshot Testing per maggiori informazioni.

Puoi fornire un argomento opzionale propertyMatchers di tipo oggetto, contenente matcher asimmetrici come valori per un sottoinsieme di proprietà attese, solo se il valore ricevuto sarà un'istanza di oggetto. È simile a toMatchObject con criteri flessibili per un sottoinsieme di proprietà, seguito da un test snapshot con criteri esatti per le proprietà rimanenti.

Puoi fornire un argomento opzionale hint (stringa) aggiunto al nome del test. Sebbene Jest aggiunga sempre un numero al nome dello snapshot, brevi suggerimenti descrittivi possono essere più utili dei numeri per differenziare più snapshot in un singolo blocco it o test. Jest ordina gli snapshot per nome nel file .snap corrispondente.

.toMatchInlineSnapshot(propertyMatchers?, inlineSnapshot)

Garantisce che un valore corrisponda allo snapshot più recente.

Puoi fornire un argomento opzionale propertyMatchers di tipo oggetto, contenente matcher asimmetrici come valori per un sottoinsieme di proprietà attese, solo se il valore ricevuto sarà un'istanza di oggetto. È simile a toMatchObject con criteri flessibili per un sottoinsieme di proprietà, seguito da un test snapshot con criteri esatti per le proprietà rimanenti.

Jest aggiunge l'argomento stringa inlineSnapshot al matcher nel file di test (anziché in un file esterno .snap) durante la prima esecuzione del test.

Consulta la sezione sugli Snapshot Inline per maggiori informazioni.

.toStrictEqual(value)

Usa .toStrictEqual per verificare che gli oggetti abbiano struttura e tipo identici.

Differenze rispetto a .toEqual:

  • Le chiavi con proprietà undefined vengono controllate, ad esempio {a: undefined, b: 2} non sarà uguale a {b: 2};

  • Gli elementi undefined sono considerati, ad esempio [2] non sarà uguale a [2, undefined];

  • La sparsità degli array è verificata, ad esempio [, 1] non sarà uguale a [undefined, 1];

  • I tipi di oggetto sono controllati, ad esempio un'istanza di classe con campi a e b non sarà uguale a un oggetto letterale con campi a e 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?)

Anche con l'alias: .toThrowError(error?)

Usa .toThrow per verificare che una funzione generi un errore quando viene chiamata. Ad esempio, per testare che drinkFlavor('octopus') generi un errore perché il gusto polipo è troppo disgustoso, scriveremmo:

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

Devi racchiudere il codice in una funzione, altrimenti l'errore non verrà catturato e l'asserzione fallirà.

Puoi fornire un argomento opzionale per verificare che venga generato un errore specifico:

  • Espressione regolare: il messaggio d'errore corrisponde al pattern

  • Stringa: il messaggio d'errore include la sottostringa

  • Oggetto errore: il messaggio d'errore è uguale alla proprietà message dell'oggetto

  • Classe errore: l'oggetto errore è istanza della classe

Ad esempio, supponiamo che drinkFlavor sia codificata così:

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

Potremmo verificare che questo errore venga generato in diversi modi:

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

Usa .toThrowErrorMatchingSnapshot per verificare che una funzione generi un errore corrispondente allo snapshot più recente quando viene chiamata.

Puoi fornire un argomento opzionale hint (stringa) aggiunto al nome del test. Sebbene Jest aggiunga sempre un numero al nome dello snapshot, brevi suggerimenti descrittivi possono essere più utili dei numeri per differenziare più snapshot in un singolo blocco it o test. Jest ordina gli snapshot per nome nel file .snap corrispondente.

Ad esempio, supponiamo che la funzione drinkFlavor generi un errore quando il gusto è 'octopus', codificata così:

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

Il test per questa funzione apparirà così:

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

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

E genererà il seguente snapshot:

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

Consulta React Tree Snapshot Testing per maggiori informazioni sullo snapshot testing.

.toThrowErrorMatchingInlineSnapshot(inlineSnapshot)

Usa .toThrowErrorMatchingInlineSnapshot per verificare che una funzione generi un errore corrispondente allo snapshot più recente quando viene chiamata.

Jest aggiunge l'argomento stringa inlineSnapshot al matcher nel file di test (anziché in un file esterno .snap) durante la prima esecuzione del test.

Consulta la sezione sugli Snapshot Inline per maggiori informazioni.

Matcher Asimmetrici

expect.anything()

expect.anything() corrisponde a qualsiasi valore tranne null o undefined. Puoi usarlo dentro toEqual o toHaveBeenCalledWith al posto di un valore letterale. Ad esempio, per verificare che una funzione mock venga chiamata con un argomento non nullo:

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) corrisponde a qualsiasi istanza creata con il costruttore specificato o ai primitivi del tipo passato. Utile dentro toEqual o toHaveBeenCalledWith al posto di valori letterali. Ad esempio, per verificare che una funzione mock venga chiamata con un numero:

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) verifica che un array ricevuto contenga tutti gli elementi dell'array atteso. L'array atteso è quindi un sottoinsieme di quello ricevuto. Possono essere presenti elementi aggiuntivi.

Puoi usarlo al posto di un valore letterale:

  • in toEqual o toHaveBeenCalledWith

  • per corrispondere a una proprietà in objectContaining o toMatchObject

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

expect.not.arrayContaining(array)

expect.not.arrayContaining(array) verifica che un array ricevuto non contenga tutti gli elementi dell'array atteso. Cioè, l'array atteso non è un sottoinsieme dell'array ricevuto.

È l'inverso di 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?) è utile per confrontare numeri floating point in proprietà di oggetti o elementi di array. Per confrontare singoli numeri usa .toBeCloseTo.

L'argomento opzionale numDigits limita il numero di cifre da verificare dopo il punto decimale. Per il valore predefinito 2, il criterio di test è Math.abs(expected - received) < 0.005 (that is, 10 ** -2 / 2).

Esempio con precisione a 5 cifre decimali:

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) verifica che un oggetto ricevuto contenga ricorsivamente tutte le proprietà attese. L'oggetto atteso è un sottoinsieme di quello ricevuto.

Nelle proprietà puoi usare altri matcher come expect.anything().

Per esempio, supponiamo di aspettarci che una funzione onPress venga chiamata con un oggetto Event, e tutto ciò che dobbiamo verificare è che l'evento abbia le proprietà event.x e event.y. Possiamo farlo con:

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) verifica che un oggetto ricevuto non contenga ricorsivamente tutte le proprietà attese.

È l'inverso di 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) corrisponde al valore ricevuto se è una stringa che contiene esattamente la stringa attesa.

expect.not.stringContaining(string)

expect.not.stringContaining(string) corrisponde al valore ricevuto se non è una stringa o se è una stringa che non contiene esattamente la stringa attesa.

È l'inverso di 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) corrisponde al valore ricevuto se è una stringa che corrisponde alla stringa o all'espressione regolare attesa.

Puoi usarlo al posto di un valore letterale:

  • in toEqual o toHaveBeenCalledWith

  • per corrispondere a un elemento in arrayContaining

  • per corrispondere a una proprietà in objectContaining o toMatchObject

Questo esempio mostra anche come puoi annidare più matcher asimmetrici, con expect.stringMatching all'interno di 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) corrisponde al valore ricevuto se non è una stringa o se è una stringa che non corrisponde alla stringa o all'espressione regolare attesa.

È l'inverso di 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));
});
});

Conteggio delle asserzioni

expect.assertions(number)

expect.assertions(number) verifica che un certo numero di asserzioni vengano chiamate durante un test. È utile quando si testa codice asincrono, per assicurarsi che le asserzioni in una callback vengano effettivamente chiamate.

Ad esempio, supponiamo di avere una funzione doAsync che riceve due callback callback1 e callback2, e le chiama in modo asincrono in un ordine sconosciuto. Possiamo testarla così:

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

doAsync(callback1, callback2);
});

La chiamata expect.assertions(2) garantisce che entrambe le callback vengano effettivamente chiamate.

expect.hasAssertions()

expect.hasAssertions() verifica che almeno un'asserzione venga chiamata durante un test. È utile quando si testa codice asincrono, per assicurarsi che le asserzioni in una callback vengano effettivamente chiamate.

Ad esempio, supponiamo di avere diverse funzioni che gestiscono uno stato. prepareState chiama una callback con un oggetto di stato, validateState opera su quell'oggetto, e waitOnState restituisce una promise che attende il completamento di tutte le callback di prepareState. Possiamo testarlo così:

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

La chiamata expect.hasAssertions() garantisce che la callback di prepareState venga effettivamente chiamata.

Utilità di estensione

expect.addEqualityTesters(testers)

Puoi usare expect.addEqualityTesters per aggiungere metodi personalizzati per verificare se due oggetti sono uguali. Ad esempio, supponiamo che nel tuo codice ci sia una classe che rappresenta un volume e può determinare se due volumi che usano unità diverse sono equivalenti. Potresti volere che toEqual (e altri matcher di uguaglianza) usino questo metodo personalizzato quando confrontano istanze della classe Volume. Puoi aggiungere un tester di uguaglianza personalizzato per far sì che toEqual rilevi e applichi la logica personalizzata quando confronta classi Volume:

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

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

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

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

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

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

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

API dei tester di uguaglianza personalizzati

I tester personalizzati sono funzioni che restituiscono il risultato (true o false) del confronto tra i due argomenti forniti, oppure undefined se il tester non gestisce gli oggetti dati e vuole delegare il confronto ad altri tester (ad esempio, i tester di uguaglianza integrati).

I tester personalizzati vengono chiamati con 3 argomenti: i due oggetti da confrontare e l'array dei tester personalizzati (usato per tester ricorsivi, vedi la sezione seguente).

Queste funzioni helper e proprietà sono disponibili su this all'interno di un tester personalizzato:

this.equals(a, b, customTesters?)

Funzione di deep equality che restituisce true se due oggetti hanno valori identici (ricorsivamente). Accetta opzionalmente una lista di tester personalizzati per uguaglianze da applicare ai controlli profondi. Se usi questa funzione, passa i tester personalizzati ricevuti affinché ulteriori controlli di uguaglianza eseguiti da equals possano utilizzare eventuali tester configurati dall'autore del test. Vedi l'esempio nella sezione Tester personalizzati ricorsivi per maggiori dettagli.

Matcher vs Tester

I matcher sono metodi disponibili su expect, come expect().toEqual(). toEqual è un matcher. Un tester è un metodo usato dai matcher che eseguono controlli di uguaglianza per determinare se gli oggetti sono identici.

I matcher personalizzati sono utili quando vuoi fornire un'asserzione personalizzata che gli autori dei test possono utilizzare. Ad esempio, l'esempio toBeWithinRange nella sezione expect.extend è un buon esempio di matcher personalizzato. A volte un autore di test potrebbe voler verificare che due numeri siano esattamente uguali e dovrebbe usare toBe. Altre volte, però, potrebbe voler consentire una certa flessibilità nel test, e toBeWithinRange potrebbe essere un'asserzione più appropriata.

I tester di uguaglianza personalizzati sono ideali per estendere globalmente i matcher di Jest applicando logiche di uguaglianza personalizzate a tutti i confronti. Gli autori dei test non possono attivare tester personalizzati per alcune asserzioni e disattivarli per altre (in tal caso andrebbe usato un matcher personalizzato). Ad esempio, definire come verificare l'uguaglianza di due oggetti Volume per tutti i matcher sarebbe un buon tester di uguaglianza personalizzato.

Tester personalizzati ricorsivi

Se i tuoi tester di uguaglianza personalizzati verificano oggetti con proprietà che richiedono deep equality, usa l'helper this.equals disponibile nei tester. Questo metodo equals è lo stesso metodo di deep equality che Jest usa internamente per tutti i confronti profondi. È il metodo che invoca il tuo tester personalizzato. Accetta un array di tester personalizzati come terzo argomento. Anche i tester personalizzati ricevono un array di tester come loro terzo argomento. Passa questo argomento nel terzo parametro di equals affinché ulteriori controlli di uguaglianza più profondi nel tuo oggetto possano sfruttare eventuali tester personalizzati.

Ad esempio, supponi di avere una classe Book che contiene un array di classi Author, entrambe con tester personalizzati. Il tester personalizzato di Book dovrebbe eseguire un controllo di deep equality sull'array di Author passando i tester personalizzati ricevuti, così che venga applicato il tester di uguaglianza personalizzato di Author:

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

Ricorda di definire i tuoi tester di uguaglianza come funzioni regolari e non arrow function per accedere agli helper del contesto (es. this.equals).

expect.addSnapshotSerializer(serializer)

Puoi chiamare expect.addSnapshotSerializer per aggiungere un modulo che formatta strutture dati specifiche dell'applicazione.

In un singolo file di test, un modulo aggiunto ha priorità rispetto ai moduli dalla configurazione snapshotSerializers, che a loro volta hanno priorità sui serializer predefiniti per tipi JavaScript built-in ed elementi React. L'ultimo modulo aggiunto è il primo ad essere testato.

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

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

Se aggiungi uno snapshot serializer in singoli file di test anziché nella configurazione snapshotSerializers:

  • Rendi la dipendenza esplicita invece che implicita

  • Eviti limiti di configurazione che potrebbero costringerti a ejectare da create-react-app

Vedi configurazione di Jest per maggiori informazioni.

expect.extend(matchers)

Puoi usare expect.extend per aggiungere i tuoi matcher personalizzati a Jest. Ad esempio, supponi di testare una libreria di utilità numeriche e di dover spesso verificare che numeri ricadano in intervalli specifici. Potresti astrarre questa logica in un matcher toBeWithinRange:

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

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

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

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

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

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

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

export {};

La dichiarazione di tipo del matcher può risiedere in un file .d.ts o in un modulo .ts importato (vedi esempi JS e TS sopra rispettivamente). Se mantieni la dichiarazione in un file .d.ts, assicurati che sia inclusa nel programma e che sia un modulo valido, cioè abbia almeno un export {} vuoto.

Invece di importare il modulo toBeWithinRange nel file di test, puoi abilitare il matcher per tutti i test spostando la chiamata expect.extend in uno script setupFilesAfterEnv:

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

expect.extend({
toBeWithinRange,
});

Matcher Asincroni

expect.extend supporta anche matcher asincroni. I matcher asincroni restituiscono una Promise, quindi dovrai attendere il valore restituito. Usiamo un matcher di esempio per illustrarne l'utilizzo. Implementeremo un matcher chiamato toBeDivisibleByExternalValue, dove il numero divisibile verrà recuperato da una fonte esterna.

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 dei Matcher Personalizzati

I matcher dovrebbero restituire un oggetto (o una Promise di un oggetto) con due chiavi. pass indica se c'è stato un match o meno, e message fornisce una funzione senza argomenti che restituisce un messaggio di errore in caso di fallimento. Quindi, quando pass è false, message dovrebbe restituire il messaggio di errore per quando expect(x).yourMatcher() fallisce. E quando pass è true, message dovrebbe restituire il messaggio di errore per quando expect(x).not.yourMatcher() fallisce.

I matcher vengono chiamati con l'argomento passato a expect(x) seguito dagli argomenti passati a .yourMatcher(y, z):

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

Queste funzioni helper e proprietà possono essere trovate su this all'interno di un matcher personalizzato:

this.isNot

Un booleano che ti informa se questo matcher è stato chiamato con il modificatore .not negato, permettendoti di visualizzare un hint chiaro e corretto (vedi codice di esempio).

this.promise

Una stringa che ti permette di visualizzare un hint chiaro e corretto per il matcher:

  • 'rejects' se il matcher è stato chiamato con il modificatore .rejects per le promise

  • 'resolves' se il matcher è stato chiamato con il modificatore .resolves per le promise

  • '' se il matcher non è stato chiamato con un modificatore per promise

this.equals(a, b, customTesters?)

Questa è una funzione di deep-equality che restituisce true se due oggetti hanno gli stessi valori (ricorsivamente). Accetta opzionalmente una lista di tester di uguaglianza personalizzati da applicare ai controlli di deep equality (vedi this.customTesters sotto).

this.expand

Un booleano che ti informa se questo matcher è stato chiamato con un'opzione expand. Quando Jest viene chiamato con il flag --expand, this.expand può essere usato per determinare se Jest dovrebbe mostrare diff ed errori completi.

this.utils

Sono disponibili diversi strumenti utili esposti in this.utils, principalmente costituiti dalle esportazioni di jest-matcher-utils.

I più utili sono matcherHint, printExpected e printReceived per formattare i messaggi di errore in modo elegante. Ad esempio, guarda l'implementazione del matcher toBe:

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

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

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

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

Questo stamperà qualcosa simile a:

  expect(received).toBe(expected)

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

Quando un'asserzione fallisce, il messaggio di errore dovrebbe fornire tutti i segnali necessari all'utente per risolvere rapidamente il problema. Dovresti creare un messaggio di fallimento preciso per garantire agli utenti delle tue asserzioni personalizzate un'esperienza di sviluppo positiva.

this.customTesters

Se il tuo matcher effettua un controllo di uguaglianza profonda utilizzando this.equals, potresti voler passare tester personalizzati forniti dall'utente a this.equals. I tester di uguaglianza personalizzati che l'utente ha fornito tramite l'API addEqualityTesters sono disponibili in questa proprietà. I matcher integrati di Jest passano this.customTesters (insieme ad altri tester predefiniti) a this.equals per eseguire controlli di uguaglianza profonda, e i tuoi matcher personalizzati potrebbero voler fare lo stesso.

Matcher personalizzati per snapshot

Per utilizzare lo snapshot testing all'interno del tuo matcher personalizzato, puoi importare jest-snapshot e utilizzarlo direttamente nel matcher.

Ecco un esempio di matcher per snapshot che tronca una stringa alla lunghezza specificata, .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"`;
*/

È anche possibile creare matcher personalizzati per snapshot inline, che verranno aggiunti correttamente ai matcher personalizzati. Tuttavia, gli snapshot inline cercheranno sempre di aggiungersi al primo argomento o al secondo quando il primo è un matcher di proprietà, quindi non è possibile accettare argomenti personalizzati nei matcher personalizzati.

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

Se il tuo matcher personalizzato per snapshot inline è asincrono (cioè utilizza async-await) potresti incontrare un errore come "Non sono supportati più snapshot inline per la stessa chiamata". Jest necessita di informazioni contestuali aggiuntive per individuare dove è stato utilizzato il matcher personalizzato e aggiornare correttamente gli snapshot.

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

Interruzione anticipata

Normalmente jest cerca di confrontare ogni snapshot previsto in un test.

A volte potrebbe non avere senso continuare il test se uno snapshot precedente è fallito. Ad esempio, quando crei snapshot di una macchina a stati dopo varie transizioni, puoi interrompere il test non appena una transizione produce uno stato errato.

In tal caso puoi implementare un matcher personalizzato per snapshot che genera un errore al primo mismatch invece di raccogliere tutti gli errori.

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