expect
I seguenti tipi sono utilizzati nelle firme dei tipi sottostanti:
type Awaitable<T> = T | PromiseLike<T>;
expect
è utilizzato per creare asserzioni. In questo contesto, le assertions
sono funzioni che possono essere invocate per verificare un'affermazione. Vitest fornisce asserzioni chai
per impostazione predefinita e anche asserzioni compatibili con Jest
, basate su chai
. A differenza di Jest
, Vitest supporta un messaggio come secondo argomento. Se l'asserzione fallisce, verrà visualizzato il messaggio di errore fornito.
export interface ExpectStatic
extends Chai.ExpectStatic,
AsymmetricMatchersContaining {
<T>(actual: T, message?: string): Assertion<T>;
extend: (expects: MatchersObject) => void;
anything: () => any;
any: (constructor: unknown) => any;
getState: () => MatcherState;
setState: (state: Partial<MatcherState>) => void;
not: AsymmetricMatchersContaining;
}
Ad esempio, questo codice asserisce che un valore input
è uguale a 2
. Se non lo è, l'asserzione lancerà un errore e il test fallirà.
import { expect } from 'vitest';
const input = Math.sqrt(4);
expect(input).to.equal(2); // API chai
expect(input).toBe(2); // API jest
Tecnicamente, questo esempio non utilizza la funzione test
; pertanto, nella console apparirà un errore di Node.js anziché l'output di Vitest. Per maggiori informazioni su test
, consulta il Riferimento API di Test.
Inoltre, expect
può essere utilizzato staticamente per accedere alle funzioni matcher, descritte più avanti, e ad altre funzionalità.
WARNING
expect
non ha alcun effetto sui tipi di test se l'espressione non presenta un errore di tipo. Se desideri utilizzare Vitest per il controllo dei tipi, usa expectTypeOf
o assertType
.
soft
- Tipo:
ExpectStatic & (actual: any) => Assertions
expect.soft
funziona in modo simile a expect
, ma invece di terminare l'esecuzione del test in caso di asserzione fallita, continua a funzionare e contrassegna il fallimento come un errore del test. Tutti gli errori riscontrati durante il test verranno visualizzati al suo completamento.
import { expect, test } from 'vitest';
test('expect.soft test', () => {
expect.soft(1 + 1).toBe(3); // contrassegna il test come fallito e prosegue
expect.soft(1 + 2).toBe(4); // contrassegna il test come fallito e prosegue
});
// il reporter riporterà entrambi gli errori alla fine dell'esecuzione
Può essere usato anche con expect
. Se l'asserzione expect
fallisce, il test verrà terminato e tutti gli errori precedenti verranno visualizzati.
import { expect, test } from 'vitest';
test('expect.soft test', () => {
expect.soft(1 + 1).toBe(3); // contrassegna il test come fallito e prosegue
expect(1 + 2).toBe(4); // il test fallisce e viene terminato, mostrando tutti gli errori precedenti
expect.soft(1 + 3).toBe(5); // non verrà eseguito
});
WARNING
expect.soft
può essere usato solo all'interno della funzione test
.
poll
interface ExpectPoll extends ExpectStatic {
(actual: () => T, options: { interval; timeout; message }): Promise<
Assertions<T>
>;
}
expect.poll
riesegue l'asserzione finché non ha successo. È possibile configurare il numero di volte in cui Vitest deve rieseguire la callback di expect.poll
impostando le opzioni interval
e timeout
.
Se viene lanciato un errore all'interno della callback di expect.poll
, Vitest riproverà finché il timeout non scade.
import { expect, test } from 'vitest';
test('element exists', async () => {
asyncInjectElement();
await expect.poll(() => document.querySelector('.element')).toBeTruthy();
});
WARNING
expect.poll
rende ogni asserzione asincrona, quindi è necessario attenderla (await
). A partire da Vitest 3, se si dimentica di attenderla, il test fallirà con un avviso.
expect.poll
non funziona con diversi matcher:
- I matcher snapshot non sono supportati perché avrebbero sempre successo. Se la tua condizione è instabile, considera l'utilizzo di
vi.waitFor
per risolverla prima:
import { expect, vi } from 'vitest';
const flakyValue = await vi.waitFor(() => getFlakyValue());
expect(flakyValue).toMatchSnapshot();
.resolves
e.rejects
non sono supportati.expect.poll
attende già la condizione se è asincrona.toThrow
e i suoi alias non sono supportati perché la condizione diexpect.poll
viene sempre risolta prima che il matcher ottenga il valore.
not
L'uso di not
negherà l'asserzione. Ad esempio, questo codice asserisce che un valore input
non è uguale a 2
. Se è uguale, l'asserzione lancerà un errore e il test fallirà.
import { expect, test } from 'vitest';
const input = Math.sqrt(16);
expect(input).not.to.equal(2); // API chai
expect(input).not.toBe(2); // API jest
toBe
- Tipo:
(value: any) => Awaitable<void>
toBe
può essere usato per asserire se i primitivi sono uguali o se gli oggetti condividono la stessa referenza. È equivalente a chiamare expect(Object.is(3, 3)).toBe(true)
. Se gli oggetti non sono gli stessi, ma si vuole controllare se le loro strutture sono identiche, si può usare toEqual
.
Ad esempio, il codice seguente controlla se il commerciante ha 13 mele.
import { expect, test } from 'vitest';
const stock = {
type: 'apples',
count: 13,
};
test('stock has 13 apples', () => {
expect(stock.type).toBe('apples');
expect(stock.count).toBe(13);
});
test('stocks are the same', () => {
const refStock = stock; // stessa referenza
expect(stock).toBe(refStock);
});
Si consiglia di non utilizzare toBe
con i numeri in virgola mobile. Poiché JavaScript li arrotonda, 0.1 + 0.2
non è strettamente 0.3
. Per asserire in modo affidabile i numeri in virgola mobile, usa l'asserzione toBeCloseTo
.
toBeCloseTo
- Tipo:
(value: number, numDigits?: number) => Awaitable<void>
Usa toBeCloseTo
per confrontare i numeri in virgola mobile. L'argomento opzionale numDigits
limita il numero di cifre da controllare dopo il punto decimale. Ad esempio:
import { expect, test } from 'vitest';
test.fails('decimals are not equal in javascript', () => {
expect(0.2 + 0.1).toBe(0.3); // 0.2 + 0.1 è 0.30000000000000004
});
test('decimals are rounded to 5 after the point', () => {
// 0.2 + 0.1 è 0.30000 | "000000000004" rimosso
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
// nulla da 0.30000000000000004 viene rimosso
expect(0.2 + 0.1).not.toBeCloseTo(0.3, 50);
});
toBeDefined
- Tipo:
() => Awaitable<void>
toBeDefined
asserisce che il valore non è uguale a undefined
. Un caso d'uso utile sarebbe verificare se una funzione ha restituito qualcosa.
import { expect, test } from 'vitest';
function getApples() {
return 3;
}
test('function returned something', () => {
expect(getApples()).toBeDefined();
});
toBeUndefined
- Tipo:
() => Awaitable<void>
Opposto di toBeDefined
, toBeUndefined
asserisce che il valore è uguale a undefined
. Un caso d'uso utile sarebbe verificare se una funzione non ha restituito nulla.
import { expect, test } from 'vitest';
function getApplesFromStock(stock: string) {
if (stock === 'Bill') {
return 13;
}
}
test("mary doesn't have a stock", () => {
expect(getApplesFromStock('Mary')).toBeUndefined();
});
toBeTruthy
- Tipo:
() => Awaitable<void>
toBeTruthy
asserisce che il valore è vero quando convertito in booleano. Utile se non si è interessati al valore, ma si desidera solo sapere che può essere convertito in true
.
Ad esempio, con questo codice non è necessario preoccuparsi del valore di ritorno di stocks.getInfo
- potrebbe essere un oggetto complesso, una stringa o qualsiasi altra cosa. Il codice funzionerà comunque.
import { Stocks } from './stocks.js';
const stocks = new Stocks();
stocks.sync('Bill');
if (stocks.getInfo('Bill')) {
stocks.sell('apples', 'Bill');
}
Quindi, se si desidera testare che stocks.getInfo
sarà truthy, si potrebbe scrivere:
import { expect, test } from 'vitest';
import { Stocks } from './stocks.js';
const stocks = new Stocks();
test('if we know Bill stock, sell apples to him', () => {
stocks.sync('Bill');
expect(stocks.getInfo('Bill')).toBeTruthy();
});
Tutto in JavaScript è truthy, eccetto false
, null
, undefined
, NaN
, 0
, -0
, 0n
, ""
e document.all
.
toBeFalsy
- Tipo:
() => Awaitable<void>
toBeFalsy
asserisce che il valore è falso quando convertito in booleano. Utile se non si è interessati al valore, ma si desidera solo sapere se può essere convertito in false
.
Ad esempio, con questo codice non è necessario preoccuparsi del valore di ritorno di stocks.stockFailed
- potrebbe restituire qualsiasi valore falsy, ma il codice funzionerà comunque.
import { Stocks } from './stocks.js';
const stocks = new Stocks();
stocks.sync('Bill');
if (!stocks.stockFailed('Bill')) {
stocks.sell('apples', 'Bill');
}
Quindi, se si desidera testare che stocks.stockFailed
sarà falsy, si potrebbe scrivere:
import { expect, test } from 'vitest';
import { Stocks } from './stocks.js';
const stocks = new Stocks();
test("if Bill stock hasn't failed, sell apples to him", () => {
stocks.syncStocks('Bill');
expect(stocks.stockFailed('Bill')).toBeFalsy();
});
Tutto in JavaScript è truthy, eccetto false
, null
, undefined
, NaN
, 0
, -0
, 0n
, ""
e document.all
.
toBeNull
- Tipo:
() => Awaitable<void>
toBeNull
asserisce semplicemente se qualcosa è null
. Alias per .toBe(null)
.
import { expect, test } from 'vitest';
function apples() {
return null;
}
test("we don't have apples", () => {
expect(apples()).toBeNull();
});
toBeNaN
- Tipo:
() => Awaitable<void>
toBeNaN
asserisce semplicemente se qualcosa è NaN
. Alias per .toBe(NaN)
.
import { expect, test } from 'vitest';
let i = 0;
function getApplesCount() {
i++;
return i > 1 ? Number.NaN : i;
}
test('getApplesCount has some unusual side effects...', () => {
expect(getApplesCount()).not.toBeNaN();
expect(getApplesCount()).toBeNaN();
});
toBeOneOf
- Tipo:
(sample: Array<any>) => any
toBeOneOf
asserisce se un valore corrisponde a uno qualsiasi dei valori nell'array fornito.
import { expect, test } from 'vitest';
test('fruit is one of the allowed values', () => {
expect(fruit).toBeOneOf(['apple', 'banana', 'orange']);
});
Il matcher asimmetrico è particolarmente utile per testare proprietà opzionali che potrebbero essere null
o undefined
:
test('optional properties can be null or undefined', () => {
const user = {
firstName: 'John',
middleName: undefined,
lastName: 'Doe',
};
expect(user).toEqual({
firstName: expect.any(String),
middleName: expect.toBeOneOf([expect.any(String), undefined]),
lastName: expect.any(String),
});
});
TIP
È possibile usare expect.not
con questo matcher per assicurarsi che un valore NON corrisponda a nessuna delle opzioni fornite.
toBeTypeOf
- Tipo:
(c: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => Awaitable<void>
toBeTypeOf
asserisce se un valore effettivo è del tipo del tipo ricevuto.
import { expect, test } from 'vitest';
const actual = 'stock';
test('stock is type of string', () => {
expect(actual).toBeTypeOf('string');
});
toBeInstanceOf
- Tipo:
(c: any) => Awaitable<void>
toBeInstanceOf
asserisce se un valore effettivo è un'istanza della classe ricevuta.
import { expect, test } from 'vitest';
import { Stocks } from './stocks.js';
const stocks = new Stocks();
test('stocks are instance of Stocks', () => {
expect(stocks).toBeInstanceOf(Stocks);
});
toBeGreaterThan
- Tipo:
(n: number | bigint) => Awaitable<void>
toBeGreaterThan
asserisce se il valore effettivo è maggiore di quello ricevuto. Valori uguali faranno fallire il test.
import { expect, test } from 'vitest';
import { getApples } from './stocks.js';
test('have more then 10 apples', () => {
expect(getApples()).toBeGreaterThan(10);
});
toBeGreaterThanOrEqual
- Tipo:
(n: number | bigint) => Awaitable<void>
toBeGreaterThanOrEqual
asserisce se il valore effettivo è maggiore di quello ricevuto o uguale ad esso.
import { expect, test } from 'vitest';
import { getApples } from './stocks.js';
test('have 11 apples or more', () => {
expect(getApples()).toBeGreaterThanOrEqual(11);
});
toBeLessThan
- Tipo:
(n: number | bigint) => Awaitable<void>
toBeLessThan
asserisce se il valore effettivo è minore di quello ricevuto. Valori uguali faranno fallire il test.
import { expect, test } from 'vitest';
import { getApples } from './stocks.js';
test('have less then 20 apples', () => {
expect(getApples()).toBeLessThan(20);
});
toBeLessThanOrEqual
- Tipo:
(n: number | bigint) => Awaitable<void>
toBeLessThanOrEqual
asserisce se il valore effettivo è minore di quello ricevuto o uguale ad esso.
import { expect, test } from 'vitest';
import { getApples } from './stocks.js';
test('have 11 apples or less', () => {
expect(getApples()).toBeLessThanOrEqual(11);
});
toEqual
- Tipo:
(received: any) => Awaitable<void>
toEqual
asserisce se il valore effettivo è uguale a quello ricevuto o ha la stessa struttura, se è un oggetto (li confronta ricorsivamente). Puoi vedere la differenza tra toEqual
e toBe
in questo esempio:
import { expect, test } from 'vitest';
const stockBill = {
type: 'apples',
count: 13,
};
const stockMary = {
type: 'apples',
count: 13,
};
test('stocks have the same properties', () => {
expect(stockBill).toEqual(stockMary);
});
test('stocks are not the same', () => {
expect(stockBill).not.toBe(stockMary);
});
WARNING
Per gli oggetti Error
, vengono confrontate anche le proprietà non enumerabili come name
, message
, cause
e AggregateError.errors
. Per Error.cause
, il confronto viene eseguito in modo asimmetrico:
// successo
expect(new Error('hi', { cause: 'x' })).toEqual(new Error('hi'));
// fallimento
expect(new Error('hi')).toEqual(new Error('hi', { cause: 'x' }));
Per testare se qualcosa è stato lanciato, usa l'asserzione toThrowError
.
toStrictEqual
- Tipo:
(received: any) => Awaitable<void>
toStrictEqual
asserisce se il valore effettivo è uguale a quello ricevuto o ha la stessa struttura se è un oggetto (li confronta ricorsivamente), e dello stesso tipo.
Differenze da .toEqual
:
- Le chiavi con proprietà
undefined
vengono controllate. Ad esempio,{a: undefined, b: 2}
non corrisponde a{b: 2}
quando si usa.toStrictEqual
. - Viene controllata la sparsità dell'array. Ad esempio,
[, 1]
non corrisponde a[undefined, 1]
quando si usa.toStrictEqual
. - I tipi di oggetto vengono controllati per essere uguali. Ad esempio, un'istanza di classe con campi
a
eb
non sarà uguale a un oggetto letterale con campia
eb
.
import { expect, test } from 'vitest';
class Stock {
constructor(type) {
this.type = type;
}
}
test('structurally the same, but semantically different', () => {
expect(new Stock('apples')).toEqual({ type: 'apples' });
expect(new Stock('apples')).not.toStrictEqual({ type: 'apples' });
});
toContain
- Tipo:
(received: string) => Awaitable<void>
toContain
asserisce se il valore effettivo è in un array. toContain
può anche controllare se una stringa è una sottostringa di un'altra stringa. Se si stanno eseguendo test in un ambiente simile a un browser, questa asserzione può anche controllare se una classe è contenuta in una classList
, o un elemento è all'interno di un altro.
import { expect, test } from 'vitest';
import { getAllFruits } from './stocks.js';
test('the fruit list contains orange', () => {
expect(getAllFruits()).toContain('orange');
const element = document.querySelector('#el');
// l'elemento ha una classe
expect(element.classList).toContain('flex');
// l'elemento è all'interno di un altro
expect(document.querySelector('#wrapper')).toContain(element);
});
toContainEqual
- Tipo:
(received: any) => Awaitable<void>
toContainEqual
asserisce se un elemento con una struttura e valori specifici è contenuto in un array. Funziona come toEqual
all'interno per ogni elemento.
import { expect, test } from 'vitest';
import { getFruitStock } from './stocks.js';
test('apple available', () => {
expect(getFruitStock()).toContainEqual({ fruit: 'apple', count: 5 });
});
toHaveLength
- Tipo:
(received: number) => Awaitable<void>
toHaveLength
asserisce se un oggetto ha una proprietà .length
ed è impostata su un certo valore numerico.
import { expect, test } from 'vitest';
test('toHaveLength', () => {
expect('abc').toHaveLength(3);
expect([1, 2, 3]).toHaveLength(3);
expect('').not.toHaveLength(3); // non ha .length di 3
expect({ length: 3 }).toHaveLength(3);
});
toHaveProperty
- Tipo:
(key: any, received?: any) => Awaitable<void>
toHaveProperty
asserisce se una proprietà alla key
di riferimento fornita esiste per un oggetto.
È possibile fornire un argomento di valore opzionale, noto anche come uguaglianza profonda, come il matcher toEqual
per confrontare il valore della proprietà ricevuta.
import { expect, test } from 'vitest';
const invoice = {
isActive: true,
'P.O': '12345',
customer: {
first_name: 'John',
last_name: 'Doe',
location: 'China',
},
total_amount: 5000,
items: [
{
type: 'apples',
quantity: 10,
},
{
type: 'oranges',
quantity: 5,
},
],
};
test('John Doe Invoice', () => {
expect(invoice).toHaveProperty('isActive'); // asserisce che la chiave esiste
expect(invoice).toHaveProperty('total_amount', 5000); // asserisce che la chiave esiste e il valore è uguale
expect(invoice).not.toHaveProperty('account'); // asserisce che questa chiave non esiste
// Riferimento profondo usando la notazione a punti
expect(invoice).toHaveProperty('customer.first_name');
expect(invoice).toHaveProperty('customer.last_name', 'Doe');
expect(invoice).not.toHaveProperty('customer.location', 'India');
// Riferimento profondo usando un array contenente la chiave
expect(invoice).toHaveProperty('items[0].type', 'apples');
expect(invoice).toHaveProperty('items.0.type', 'apples'); // funziona anche la notazione a punti
// Riferimento profondo usando un array contenente il keyPath
expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples');
expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples'); // funziona anche la notazione stringa
// Avvolgi la tua chiave in un array per evitare che la chiave venga analizzata come un riferimento profondo
expect(invoice).toHaveProperty(['P.O'], '12345');
});
toMatch
- Tipo:
(received: string | regexp) => Awaitable<void>
toMatch
asserisce se una stringa corrisponde a un'espressione regolare o a una stringa.
import { expect, test } from 'vitest';
test('top fruits', () => {
expect('top fruits include apple, orange and grape').toMatch(/apple/);
expect('applefruits').toMatch('fruit'); // toMatch accetta anche una stringa
});
toMatchObject
- Tipo:
(received: object | array) => Awaitable<void>
toMatchObject
asserisce se un oggetto corrisponde a un sottoinsieme delle proprietà di un oggetto.
È anche possibile passare un array di oggetti. Questo è utile se si vuole controllare che due array corrispondano nel loro numero di elementi, a differenza di arrayContaining
, che consente elementi extra nell'array ricevuto.
import { expect, test } from 'vitest';
const johnInvoice = {
isActive: true,
customer: {
first_name: 'John',
last_name: 'Doe',
location: 'China',
},
total_amount: 5000,
items: [
{
type: 'apples',
quantity: 10,
},
{
type: 'oranges',
quantity: 5,
},
],
};
const johnDetails = {
customer: {
first_name: 'John',
last_name: 'Doe',
location: 'China',
},
};
test('invoice has john personal details', () => {
expect(johnInvoice).toMatchObject(johnDetails);
});
test('the number of elements must match exactly', () => {
// Asserisce che un array di oggetti corrisponde
expect([{ foo: 'bar' }, { baz: 1 }]).toMatchObject([
{ foo: 'bar' },
{ baz: 1 },
]);
});
toThrowError
Tipo:
(received: any) => Awaitable<void>
Alias:
toThrow
toThrowError
asserisce se una funzione lancia un errore quando viene chiamata.
È possibile fornire un argomento opzionale per testare che venga lanciato un errore specifico:
RegExp
: il messaggio di errore corrisponde al patternstring
: il messaggio di errore include la sottostringaError
,AsymmetricMatcher
: confronta con un oggetto ricevuto simile atoEqual(received)
TIP
È necessario racchiudere il codice in una funzione, altrimenti l'errore non verrà catturato e il test fallirà.
Questo non si applica alle chiamate asincrone poiché rejects scompone correttamente la promessa:
test('expect rejects toThrow', async ({ expect }) => {
const promise = Promise.reject(new Error('Test'));
await expect(promise).rejects.toThrowError();
});
Ad esempio, se si desidera testare che getFruitStock('pineapples')
lancia un errore, si potrebbe scrivere:
import { expect, test } from 'vitest';
function getFruitStock(type: string) {
if (type === 'pineapples') {
throw new Error('Pineapples are not in stock');
}
// Fai altre cose
}
test('throws on pineapples', () => {
// Testa che il messaggio di errore contenga "stock" da qualche parte: questi sono equivalenti
expect(() => getFruitStock('pineapples')).toThrowError(/stock/);
expect(() => getFruitStock('pineapples')).toThrowError('stock');
// Testa il messaggio di errore esatto
expect(() => getFruitStock('pineapples')).toThrowError(
/^Pineapples are not in stock$/
);
expect(() => getFruitStock('pineapples')).toThrowError(
new Error('Pineapples are not in stock')
);
expect(() => getFruitStock('pineapples')).toThrowError(
expect.objectContaining({
message: 'Pineapples are not in stock',
})
);
});
TIP
Per testare funzioni asincrone, usa in combinazione con rejects.
function getAsyncFruitStock() {
return Promise.reject(new Error('empty'));
}
test('throws on pineapples', async () => {
await expect(() => getAsyncFruitStock()).rejects.toThrowError('empty');
});
toMatchSnapshot
- Tipo:
<T>(shape?: Partial<T> | string, hint?: string) => void
Questo assicura che un valore corrisponda allo snapshot più recente.
È possibile fornire un argomento stringa hint
opzionale che viene aggiunto al nome del test. Sebbene Vitest aggiunga sempre un numero alla fine di un nome di snapshot, brevi suggerimenti descrittivi potrebbero essere più utili dei numeri per differenziare più snapshot in un singolo blocco it
o test
. Vitest ordina gli snapshot per nome nel file .snap
corrispondente.
TIP
Quando uno snapshot non corrisponde e causa il fallimento del test, se la mancata corrispondenza è prevista, puoi premere il tasto u
per aggiornare lo snapshot una volta. Oppure puoi passare le opzioni CLI -u
o --update
per fare in modo che Vitest aggiorni sempre i test.
import { expect, test } from 'vitest';
test('matches snapshot', () => {
const data = { foo: new Set(['bar', 'snapshot']) };
expect(data).toMatchSnapshot();
});
È anche possibile fornire una forma di un oggetto, se si sta testando solo la forma di un oggetto e non è necessario che sia compatibile al 100%:
import { expect, test } from 'vitest';
test('matches snapshot', () => {
const data = { foo: new Set(['bar', 'snapshot']) };
expect(data).toMatchSnapshot({ foo: expect.any(Set) });
});
toMatchInlineSnapshot
- Tipo:
<T>(shape?: Partial<T> | string, snapshot?: string, hint?: string) => void
Questo assicura che un valore corrisponda allo snapshot più recente.
Vitest aggiunge e aggiorna l'argomento stringa inlineSnapshot
al matcher nel file di test (invece di un file .snap
esterno).
import { expect, test } from 'vitest';
test('matches inline snapshot', () => {
const data = { foo: new Set(['bar', 'snapshot']) };
// Vitest aggiornerà il seguente contenuto durante l'aggiornamento dello snapshot
expect(data).toMatchInlineSnapshot(`
{
"foo": Set {
"bar",
"snapshot",
},
}
`);
});
È anche possibile fornire una forma di un oggetto, se si sta testando solo la forma di un oggetto e non è necessario che sia compatibile al 100%:
import { expect, test } from 'vitest';
test('matches snapshot', () => {
const data = { foo: new Set(['bar', 'snapshot']) };
expect(data).toMatchInlineSnapshot(
{ foo: expect.any(Set) },
`
{
"foo": Any<Set>,
}
`
);
});
toMatchFileSnapshot
- Tipo:
<T>(filepath: string, hint?: string) => Promise<void>
Confronta o aggiorna lo snapshot con il contenuto di un file esplicitamente specificato (invece del file .snap
).
import { expect, it } from 'vitest';
it('render basic', async () => {
const result = renderHTML(h('div', { class: 'foo' }));
await expect(result).toMatchFileSnapshot('./test/basic.output.html');
});
Nota che poiché l'operazione del file system è asincrona, è necessario usare await
con toMatchFileSnapshot()
. Se await
non viene usato, Vitest lo tratta come expect.soft
, il che significa che il codice dopo l'istruzione continuerà a essere eseguito anche se lo snapshot non corrisponde. Dopo che il test è terminato, Vitest controllerà lo snapshot e fallirà se c'è una mancata corrispondenza.
toThrowErrorMatchingSnapshot
- Tipo:
(hint?: string) => void
Uguale a toMatchSnapshot
, ma si aspetta lo stesso valore di toThrowError
.
toThrowErrorMatchingInlineSnapshot
- Tipo:
(snapshot?: string, hint?: string) => void
Uguale a toMatchInlineSnapshot
, ma si aspetta lo stesso valore di toThrowError
.
toHaveBeenCalled
- Tipo:
() => Awaitable<void>
Questa asserzione è utile per testare che una funzione sia stata chiamata. Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
const market = {
buy(subject: string, amount: number) {
// ...
},
};
test('spy function', () => {
const buySpy = vi.spyOn(market, 'buy');
expect(buySpy).not.toHaveBeenCalled();
market.buy('apples', 10);
expect(buySpy).toHaveBeenCalled();
});
toHaveBeenCalledTimes
- Tipo:
(amount: number) => Awaitable<void>
Questa asserzione controlla se una funzione è stata chiamata un certo numero di volte. Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
const market = {
buy(subject: string, amount: number) {
// ...
},
};
test('spy function called two times', () => {
const buySpy = vi.spyOn(market, 'buy');
market.buy('apples', 10);
market.buy('apples', 20);
expect(buySpy).toHaveBeenCalledTimes(2);
});
toHaveBeenCalledWith
- Tipo:
(...args: any[]) => Awaitable<void>
Questa asserzione controlla se una funzione è stata chiamata almeno una volta con determinati parametri. Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
const market = {
buy(subject: string, amount: number) {
// ...
},
};
test('spy function', () => {
const buySpy = vi.spyOn(market, 'buy');
market.buy('apples', 10);
market.buy('apples', 20);
expect(buySpy).toHaveBeenCalledWith('apples', 10);
expect(buySpy).toHaveBeenCalledWith('apples', 20);
});
toHaveBeenCalledBefore 3.0.0+
- Tipo:
(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable<void>
Questa asserzione controlla se un Mock
è stato chiamato prima di un altro Mock
.
test('calls mock1 before mock2', () => {
const mock1 = vi.fn();
const mock2 = vi.fn();
mock1();
mock2();
mock1();
expect(mock1).toHaveBeenCalledBefore(mock2);
});
toHaveBeenCalledAfter 3.0.0+
- Tipo:
(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable<void>
Questa asserzione controlla se un Mock
è stato chiamato dopo un altro Mock
.
test('calls mock1 after mock2', () => {
const mock1 = vi.fn();
const mock2 = vi.fn();
mock2();
mock1();
mock2();
expect(mock1).toHaveBeenCalledAfter(mock2);
});
toHaveBeenCalledExactlyOnceWith 3.0.0+
- Tipo:
(...args: any[]) => Awaitable<void>
Questa asserzione controlla se una funzione è stata chiamata esattamente una volta e con determinati parametri. Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
const market = {
buy(subject: string, amount: number) {
// ...
},
};
test('spy function', () => {
const buySpy = vi.spyOn(market, 'buy');
market.buy('apples', 10);
expect(buySpy).toHaveBeenCalledExactlyOnceWith('apples', 10);
});
toHaveBeenLastCalledWith
- Tipo:
(...args: any[]) => Awaitable<void>
Questa asserzione controlla se una funzione è stata chiamata con determinati parametri nella sua ultima invocazione. Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
const market = {
buy(subject: string, amount: number) {
// ...
},
};
test('spy function', () => {
const buySpy = vi.spyOn(market, 'buy');
market.buy('apples', 10);
market.buy('apples', 20);
expect(buySpy).not.toHaveBeenLastCalledWith('apples', 10);
expect(buySpy).toHaveBeenLastCalledWith('apples', 20);
});
toHaveBeenNthCalledWith
- Tipo:
(time: number, ...args: any[]) => Awaitable<void>
Questa asserzione controlla se una funzione è stata chiamata con determinati parametri in un certo momento. Il conteggio inizia da 1. Quindi, per controllare la seconda voce, si scriverebbe .toHaveBeenNthCalledWith(2, ...)
.
Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
const market = {
buy(subject: string, amount: number) {
// ...
},
};
test('first call of spy function called with right params', () => {
const buySpy = vi.spyOn(market, 'buy');
market.buy('apples', 10);
market.buy('apples', 20);
expect(buySpy).toHaveBeenNthCalledWith(1, 'apples', 10);
});
toHaveReturned
- Tipo:
() => Awaitable<void>
Questa asserzione controlla se una funzione ha restituito con successo un valore almeno una volta (cioè, non ha lanciato un errore). Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
function getApplesPrice(amount: number) {
const PRICE = 10;
return amount * PRICE;
}
test('spy function returned a value', () => {
const getPriceSpy = vi.fn(getApplesPrice);
const price = getPriceSpy(10);
expect(price).toBe(100);
expect(getPriceSpy).toHaveReturned();
});
toHaveReturnedTimes
- Tipo:
(amount: number) => Awaitable<void>
Questa asserzione controlla se una funzione ha restituito con successo un valore un numero esatto di volte (cioè, non ha lanciato un errore). Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
test('spy function returns a value two times', () => {
const sell = vi.fn((product: string) => ({ product }));
sell('apples');
sell('bananas');
expect(sell).toHaveReturnedTimes(2);
});
toHaveReturnedWith
- Tipo:
(returnValue: any) => Awaitable<void>
È possibile invocare questa asserzione per verificare se una funzione ha restituito con successo un valore con determinati parametri almeno una volta. Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
test('spy function returns a product', () => {
const sell = vi.fn((product: string) => ({ product }));
sell('apples');
expect(sell).toHaveReturnedWith({ product: 'apples' });
});
toHaveLastReturnedWith
- Tipo:
(returnValue: any) => Awaitable<void>
È possibile invocare questa asserzione per verificare se una funzione ha restituito con successo un certo valore quando è stata invocata l'ultima volta. Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
test('spy function returns bananas on a last call', () => {
const sell = vi.fn((product: string) => ({ product }));
sell('apples');
sell('bananas');
expect(sell).toHaveLastReturnedWith({ product: 'bananas' });
});
toHaveNthReturnedWith
- Tipo:
(time: number, returnValue: any) => Awaitable<void>
È possibile invocare questa asserzione per verificare se una funzione ha restituito con successo un valore con determinati parametri in una specifica chiamata. Richiede che una funzione spia venga passata a expect
.
import { expect, test, vi } from 'vitest';
test('spy function returns bananas on second call', () => {
const sell = vi.fn((product: string) => ({ product }));
sell('apples');
sell('bananas');
expect(sell).toHaveNthReturnedWith(2, { product: 'bananas' });
});
toHaveResolved
- Tipo:
() => Awaitable<void>
Questa asserzione controlla se una funzione ha risolto con successo un valore almeno una volta (cioè, non ha rifiutato). Richiede che una funzione spia venga passata a expect
.
Se la funzione ha restituito una promessa, ma questa non è stata ancora risolta, l'asserzione fallirà.
import { expect, test, vi } from 'vitest';
import db from './db/apples.js';
async function getApplesPrice(amount: number) {
return amount * (await db.get('price'));
}
test('spy function resolved a value', async () => {
const getPriceSpy = vi.fn(getApplesPrice);
const price = await getPriceSpy(10);
expect(price).toBe(100);
expect(getPriceSpy).toHaveResolved();
});
toHaveResolvedTimes
- Tipo:
(amount: number) => Awaitable<void>
Questa asserzione controlla se una funzione ha risolto con successo un valore un numero esatto di volte (cioè, non ha rifiutato). Richiede che una funzione spia venga passata a expect
.
Questo conterà solo le promesse che sono state risolte. Se la funzione ha restituito una promessa, ma non è stata ancora risolta, non verrà contata.
import { expect, test, vi } from 'vitest';
test('spy function resolved a value two times', async () => {
const sell = vi.fn((product: string) => Promise.resolve({ product }));
await sell('apples');
await sell('bananas');
expect(sell).toHaveResolvedTimes(2);
});
toHaveResolvedWith
- Tipo:
(returnValue: any) => Awaitable<void>
È possibile invocare questa asserzione per verificare se una funzione ha risolto con successo un certo valore almeno una volta. Richiede che una funzione spia venga passata a expect
.
Se la funzione ha restituito una promessa, ma questa non è stata ancora risolta, l'asserzione fallirà.
import { expect, test, vi } from 'vitest';
test('spy function resolved a product', async () => {
const sell = vi.fn((product: string) => Promise.resolve({ product }));
await sell('apples');
expect(sell).toHaveResolvedWith({ product: 'apples' });
});
toHaveLastResolvedWith
- Tipo:
(returnValue: any) => Awaitable<void>
È possibile invocare questa asserzione per verificare se una funzione ha risolto con successo un certo valore quando è stata invocata l'ultima volta. Richiede che una funzione spia venga passata a expect
.
Se la funzione ha restituito una promessa, ma questa non è stata ancora risolta, l'asserzione fallirà.
import { expect, test, vi } from 'vitest';
test('spy function resolves bananas on a last call', async () => {
const sell = vi.fn((product: string) => Promise.resolve({ product }));
await sell('apples');
await sell('bananas');
expect(sell).toHaveLastResolvedWith({ product: 'bananas' });
});
toHaveNthResolvedWith
- Tipo:
(time: number, returnValue: any) => Awaitable<void>
È possibile invocare questa asserzione per verificare se una funzione ha risolto con successo un certo valore in una specifica invocazione. Richiede che una funzione spia venga passata a expect
.
Se la funzione ha restituito una promessa, ma questa non è stata ancora risolta, l'asserzione fallirà.
import { expect, test, vi } from 'vitest';
test('spy function returns bananas on second call', async () => {
const sell = vi.fn((product: string) => Promise.resolve({ product }));
await sell('apples');
await sell('bananas');
expect(sell).toHaveNthResolvedWith(2, { product: 'bananas' });
});
toSatisfy
- Tipo:
(predicate: (value: any) => boolean) => Awaitable<void>
Questa asserzione controlla se un valore soddisfa un certo predicato.
import { describe, expect, it } from 'vitest';
const isOdd = (value: number) => value % 2 !== 0;
describe('toSatisfy()', () => {
it('pass with 0', () => {
expect(1).toSatisfy(isOdd);
});
it('pass with negation', () => {
expect(2).not.toSatisfy(isOdd);
});
});
resolves
- Tipo:
Promisify<Assertions>
resolves
è inteso a semplificare il codice quando si asserisce codice asincrono. Utilizzalo per estrarre il valore dalla promessa in sospeso e asserirne il valore con le consuete asserzioni. Se la promessa viene rifiutata, l'asserzione fallirà.
Restituisce lo stesso oggetto Assertions
, ma tutti i matcher ora restituiscono Promise
, quindi è necessario attenderlo. Funziona anche con le asserzioni chai
.
Ad esempio, se si ha una funzione che effettua una chiamata API e restituisce alcuni dati, si potrebbe usare questo codice per asserire il suo valore di ritorno:
import { expect, test } from 'vitest';
async function buyApples() {
return fetch('/buy/apples').then(r => r.json());
}
test('buyApples returns new stock id', async () => {
// toEqual ora restituisce una promessa, quindi è necessario attenderla
await expect(buyApples()).resolves.toEqual({ id: 1 }); // API jest
await expect(buyApples()).resolves.to.equal({ id: 1 }); // API chai
});
WARNING
Se l'asserzione non viene attesa, si avrà un test falso positivo che passerà ogni volta. Per assicurarsi che le asserzioni vengano effettivamente chiamate, si può usare expect.assertions(number)
.
A partire da Vitest 3, se un metodo non viene atteso, Vitest mostrerà un avviso alla fine del test. In Vitest 4, il test verrà contrassegnato come "fallito" se l'asserzione non viene attesa.
rejects
- Tipo:
Promisify<Assertions>
rejects
è inteso a semplificare il codice quando si asserisce codice asincrono. Utilizzalo per estrarre il motivo per cui la promessa è stata rifiutata e asserirne il valore con le consuete asserzioni. Se la promessa si risolve con successo, l'asserzione fallirà.
Restituisce lo stesso oggetto Assertions
, ma tutti i matcher ora restituiscono Promise
, quindi è necessario attenderlo. Funziona anche con le asserzioni chai
.
Ad esempio, se si ha una funzione che fallisce quando la si chiama, si potrebbe usare questo codice per asserire il motivo:
import { expect, test } from 'vitest';
async function buyApples(id) {
if (!id) {
throw new Error('no id');
}
}
test('buyApples throws an error when no id provided', async () => {
// toThrow ora restituisce una promessa, quindi è necessario attenderla
await expect(buyApples()).rejects.toThrow('no id');
});
WARNING
Se l'asserzione non viene attesa, si avrà un test falso positivo che passerà ogni volta. Per assicurarsi che le asserzioni siano state effettivamente chiamate, si può usare expect.assertions(number)
.
A partire da Vitest 3, se un metodo non viene atteso, Vitest mostrerà un avviso alla fine del test. In Vitest 4, il test verrà contrassegnato come "fallito" se l'asserzione non viene attesa.
expect.assertions
- Tipo:
(count: number) => void
Dopo che il test è passato o fallito, si verifica che un certo numero di asserzioni sia stato chiamato durante il test. Un caso utile sarebbe verificare se un codice asincrono è stato chiamato.
Ad esempio, se abbiamo una funzione che chiama asincronamente due matcher, possiamo asserire che siano stati effettivamente chiamati.
import { expect, test } from 'vitest';
async function doAsync(...cbs) {
await Promise.all(cbs.map((cb, index) => cb({ index })));
}
test('all assertions are called', async () => {
expect.assertions(2);
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}
await doAsync(callback1, callback2);
});
WARNING
Quando si usano assertions
con test asincroni concorrenti, expect
dal Contesto di Test locale deve essere usato per assicurarsi che venga rilevato il test corretto.
expect.hasAssertions
- Tipo:
() => void
Dopo che il test è passato o fallito, si verifica che almeno un'asserzione sia stata chiamata durante il test. Un caso utile sarebbe verificare se un codice asincrono è stato chiamato.
Ad esempio, se si dispone di un codice che invoca una callback, è possibile effettuare un'asserzione all'interno di una callback, ma il test passerà sempre se non si verifica se un'asserzione è stata chiamata.
import { expect, test } from 'vitest';
import { db } from './db.js';
const cbs = [];
function onSelect(cb) {
cbs.push(cb);
}
// dopo aver selezionato dal db, chiamiamo tutte le callback
function select(id) {
return db.select({ id }).then(data => {
return Promise.all(cbs.map(cb => cb(data)));
});
}
test('callback was called', async () => {
expect.hasAssertions();
onSelect(data => {
// dovrebbe essere chiamato su select
expect(data).toBeTruthy();
});
// se non atteso, il test fallirà
// se non hai expect.hasAssertions(), il test passerà
await select(3);
});
expect.unreachable
- Tipo:
(message?: string) => never
Questo metodo viene utilizzato per asserire che una determinata riga di codice non dovrebbe mai essere raggiunta.
Ad esempio, se si desidera testare che build()
lancia un errore a causa di directory che non contengono una cartella src
, e gestire ogni errore separatamente, si potrebbe procedere come segue:
import { expect, test } from 'vitest';
async function build(dir) {
if (dir.includes('no-src')) {
throw new Error(`${dir}/src does not exist`);
}
}
const errorDirs = [
'no-src-folder',
// ...
];
test.each(errorDirs)('build fails with "%s"', async dir => {
try {
await build(dir);
expect.unreachable('Should not pass build');
} catch (err: any) {
expect(err).toBeInstanceOf(Error);
expect(err.stack).toContain('build');
switch (dir) {
case 'no-src-folder':
expect(err.message).toBe(`${dir}/src does not exist`);
break;
default:
// per esaurire tutti i test di errore
expect.unreachable('All error test must be handled');
break;
}
}
});
expect.anything
- Tipo:
() => any
Questo matcher asimmetrico, se utilizzato con un controllo di uguaglianza, restituirà sempre true
. Utile se si desidera solo essere sicuri che la proprietà esista.
import { expect, test } from 'vitest';
test('object has "apples" key', () => {
expect({ apples: 22 }).toEqual({ apples: expect.anything() });
});
expect.any
- Tipo:
(constructor: unknown) => any
Questo matcher asimmetrico, se utilizzato con un controllo di uguaglianza, restituirà true
solo se il valore è un'istanza di un costruttore specificato. Utile se si dispone di un valore che viene generato ogni volta e si desidera solo sapere che esiste con un tipo appropriato.
import { expect, test } from 'vitest';
import { generateId } from './generators.js';
test('"id" is a number', () => {
expect({ id: generateId() }).toEqual({ id: expect.any(Number) });
});
expect.closeTo
- Tipo:
(expected: any, precision?: number) => any
expect.closeTo
è utile quando si confrontano numeri in virgola mobile nelle proprietà degli oggetti o negli elementi degli array. Se è necessario confrontare un numero, utilizzare invece .toBeCloseTo
.
L'argomento opzionale precision
limita il numero di cifre da controllare dopo il punto decimale. Per il valore predefinito 2
, il criterio di test è Math.abs(expected - received) < 0.005 (cioè, 10 ** -2 / 2)
.
Ad esempio, questo test passa con una precisione di 5 cifre:
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.arrayContaining
- Tipo:
<T>(expected: T[]) => any
Se utilizzato con un controllo di uguaglianza, questo matcher asimmetrico restituirà true
se il valore è un array e contiene gli elementi specificati.
import { expect, test } from 'vitest';
test('basket includes fuji', () => {
const basket = {
varieties: ['Empire', 'Fuji', 'Gala'],
count: 3,
};
expect(basket).toEqual({
count: 3,
varieties: expect.arrayContaining(['Fuji']),
});
});
TIP
È possibile usare expect.not
con questo matcher per negare il valore atteso.
expect.objectContaining
- Tipo:
(expected: any) => any
Se utilizzato con un controllo di uguaglianza, questo matcher asimmetrico restituirà true
se il valore ha una forma simile.
import { expect, test } from 'vitest';
test('basket has empire apples', () => {
const basket = {
varieties: [
{
name: 'Empire',
count: 1,
},
],
};
expect(basket).toEqual({
varieties: [expect.objectContaining({ name: 'Empire' })],
});
});
TIP
È possibile usare expect.not
con questo matcher per negare il valore atteso.
expect.stringContaining
- Tipo:
(expected: any) => any
Se utilizzato con un controllo di uguaglianza, questo matcher asimmetrico restituirà true
se il valore è una stringa e contiene una sottostringa specificata.
import { expect, test } from 'vitest';
test('variety has "Emp" in its name', () => {
const variety = {
name: 'Empire',
count: 1,
};
expect(variety).toEqual({
name: expect.stringContaining('Emp'),
count: 1,
});
});
TIP
È possibile usare expect.not
con questo matcher per negare il valore atteso.
expect.stringMatching
- Tipo:
(expected: any) => any
Se utilizzato con un controllo di uguaglianza, questo matcher asimmetrico restituirà true
se il valore è una stringa e contiene una sottostringa specificata o se la stringa corrisponde a un'espressione regolare.
import { expect, test } from 'vitest';
test('variety ends with "re"', () => {
const variety = {
name: 'Empire',
count: 1,
};
expect(variety).toEqual({
name: expect.stringMatching(/re$/),
count: 1,
});
});
TIP
È possibile usare expect.not
con questo matcher per negare il valore atteso.
expect.addSnapshotSerializer
- Tipo:
(plugin: PrettyFormatPlugin) => void
Questo metodo aggiunge serializzatori personalizzati che vengono chiamati durante la creazione di uno snapshot. Questa è una funzionalità avanzata; per maggiori informazioni, consulta la guida sui serializzatori personalizzati.
Se si stanno aggiungendo serializzatori personalizzati, si dovrebbe invocare questo metodo all'interno di setupFiles
. Ciò influenzerà ogni snapshot.
TIP
Se in precedenza si è usato Vue CLI con Jest, si potrebbe voler installare jest-serializer-vue. Altrimenti, i tuoi snapshot saranno racchiusi in una stringa, il che causerà l'escape di "
.
expect.extend
- Tipo:
(matchers: MatchersObject) => void
È possibile estendere i matcher predefiniti con i propri. Questa funzione viene utilizzata per estendere l'oggetto matcher con matcher personalizzati.
Quando si definiscono i matcher in questo modo, si creano anche matcher asimmetrici che possono essere utilizzati come expect.stringContaining
.
import { expect, test } from 'vitest';
test('custom matchers', () => {
expect.extend({
toBeFoo: (received, expected) => {
if (received !== 'foo') {
return {
message: () => `expected ${received} to be foo`,
pass: false,
};
}
},
});
expect('foo').toBeFoo();
expect({ foo: 'foo' }).toEqual({ foo: expect.toBeFoo() });
});
TIP
Se si desidera che i propri matcher appaiano in ogni test, si dovrebbe invocare questo metodo all'interno di setupFiles
.
Questa funzione è compatibile con expect.extend
di Jest, quindi qualsiasi libreria che la usa per creare matcher personalizzati funzionerà con Vitest.
Se si sta utilizzando TypeScript, a partire da Vitest 0.31.0 è possibile estendere l'interfaccia Assertion
predefinita in un file di dichiarazione ambientale (ad esempio: vitest.d.ts
) con il codice seguente:
interface CustomMatchers<R = unknown> {
toBeFoo: () => R;
}
declare module 'vitest' {
interface Assertion<T = any> extends CustomMatchers<T> {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}
WARNING
Non dimenticare di includere il file di dichiarazione ambientale nel proprio tsconfig.json
.
TIP
Per maggiori informazioni, consulta la guida sull'estensione dei matcher.
expect.addEqualityTesters
- Tipo:
(tester: Array<Tester>) => void
È possibile utilizzare questo metodo per definire tester personalizzati, che sono metodi impiegati dai matcher, per verificare se due oggetti sono uguali. È compatibile con il metodo expect.addEqualityTesters
di Jest.
import { expect, test } from 'vitest';
class AnagramComparator {
public word: string;
constructor(word: string) {
this.word = word;
}
equals(other: AnagramComparator): boolean {
const cleanStr1 = this.word.replace(/ /g, '').toLowerCase();
const cleanStr2 = other.word.replace(/ /g, '').toLowerCase();
const sortedStr1 = cleanStr1.split('').sort().join('');
const sortedStr2 = cleanStr2.split('').sort().join('');
return sortedStr1 === sortedStr2;
}
}
function isAnagramComparator(a: unknown): a is AnagramComparator {
return a instanceof AnagramComparator;
}
function areAnagramsEqual(a: unknown, b: unknown): boolean | undefined {
const isAAnagramComparator = isAnagramComparator(a);
const isBAnagramComparator = isAnagramComparator(b);
if (isAAnagramComparator && isBAnagramComparator) {
return a.equals(b);
} else if (isAAnagramComparator === isBAnagramComparator) {
return undefined;
} else {
return false;
}
}
expect.addEqualityTesters([areAnagramsEqual]);
test('custom equality tester', () => {
expect(new AnagramComparator('listen')).toEqual(
new AnagramComparator('silent')
);
});