expect
I seguenti tipi sono utilizzati nelle definizioni di tipo sottostanti:
type Awaitable<T> = T | PromiseLike<T>;
expect
viene utilizzato per creare asserzioni. In questo contesto, le asserzioni
sono funzioni che vengono invocate per verificare un'affermazione. Vitest fornisce asserzioni chai
di default, e anche asserzioni compatibili con Jest
basate su chai
.
Ad esempio, questo codice asserisce che un valore input
sia uguale a 2
. In caso contrario, 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
Questo esempio non utilizza la funzione test
, quindi nella console verrà visualizzato un errore di Node.js invece dell'output di Vitest. Per saperne di più su test
, consulta Test API Reference.
Inoltre, expect
può essere usato staticamente per accedere alle funzioni dei matcher, descritte più avanti, e altro ancora.
WARNING
expect
non influisce sui tipi di test, a meno che l'espressione non contenga un errore di tipo. Se si desidera utilizzare Vitest come verificatore di tipo, utilizzare expectTypeOf
o assertType
.
soft
- Tipo:
ExpectStatic & (actual: any) => Assertions
expect.soft
funziona in modo simile a expect
, ma, in caso di fallimento dell'asserzione, invece di terminare l'esecuzione del test, continua e segnala l'errore come un fallimento del test. Tutti gli errori riscontrati durante il test verranno visualizzati al termine del test.
import { expect, test } from 'vitest';
test('expect.soft test', () => {
expect.soft(1 + 1).toBe(3); // segnala il fallimento ma continua
expect.soft(1 + 2).toBe(4); // segnala il fallimento ma continua
});
// Alla fine del test, gli errori verranno visualizzati.
Può essere usato anche con expect
. Se l'asserzione expect
fallisce, il test viene terminato e tutti gli errori vengono visualizzati.
import { expect, test } from 'vitest';
test('expect.soft test', () => {
expect.soft(1 + 1).toBe(3); // segnala il fallimento ma continua
expect(1 + 2).toBe(4); // fallisce e interrompe il test, tutti gli errori precedenti verranno visualizzati
expect.soft(1 + 3).toBe(5); // non viene eseguito
});
WARNING
expect.soft
può essere usato solo all'interno della funzione test
.
not
L'uso di not
nega l'asserzione. Ad esempio, questo codice asserisce che un valore input
non sia uguale a 2
. Se lo è, 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 tipi primitivi sono uguali o se gli oggetti condividono lo stesso riferimento. È equivalente a chiamare expect(Object.is(3, 3)).toBe(true)
. Se gli oggetti non sono identici, ma si vuole verificare se le loro strutture sono uguali, si può usare toEqual
.
Ad esempio, il codice qui sotto 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; // stesso riferimento
expect(stock).toBe(refStock);
});
È consigliabile non usare toBe
con numeri in virgola mobile. Poiché JavaScript li approssima, 0.1 + 0.2
non è esattamente 0.3
. Per asserire in modo affidabile numeri in virgola mobile, usa l'asserzione toBeCloseTo
.
toBeCloseTo
- Tipo:
(value: number, numDigits?: number) => Awaitable<void>
Usa toBeCloseTo
per confrontare numeri in virgola mobile. L'argomento opzionale numDigits
limita il numero di cifre da controllare dopo la virgola. 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);
// niente 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
. È utile per verificare se una funzione ha restituito un valore.
import { expect, test } from 'vitest';
function getApples() {
return 3;
}
test('function returned something', () => {
expect(getApples()).toBeDefined();
});
toBeUndefined
- Tipo:
() => Awaitable<void>
Al contrario di toBeDefined
, toBeUndefined
verifica che il valore sia uguale a undefined
. Un caso d'uso utile è verificare se una funzione non ha restituito alcun valore.
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 specifico, ma si vuole solo sapere se può essere convertito in true
.
Ad esempio, avendo questo codice non si è interessati al 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 vuoi testare che stocks.getInfo
sarà truthy, potresti 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();
});
In JavaScript tutti i valori sono considerati 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 specifico, ma si vuole solo sapere se può essere convertito in false
.
Ad esempio, avendo questo codice non si è interessati al 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 vuoi testare che stocks.stockFailed
sarà falsy, potresti 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();
});
In JavaScript tutti i valori sono considerati truthy, eccetto false
, null
, undefined
, NaN
, 0
, -0
, 0n
, ""
e document.all
.
toBeNull
- Tipo:
() => Awaitable<void>
toBeNull
verifica semplicemente se un valore è 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
verifica semplicemente se un valore è 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();
});
toBeTypeOf
- Tipo:
(c: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => Awaitable<void>
toBeTypeOf
verifica se il tipo di un valore corrisponde al tipo specificato.
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
verifica se un valore è un'istanza della classe specificata.
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
verifica se un valore è maggiore di un altro. Valori uguali causeranno il fallimento del 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
verifica se un valore è maggiore o uguale a un altro.
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
verifica se un valore è minore di un altro. Valori uguali causeranno il fallimento del 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
verifica se un valore è minore o uguale a un altro.
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
verifica se un valore è uguale a un altro, o se ha la stessa struttura nel caso di oggetti (confronto ricorsivo). 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
Non verrà eseguito un controllo di uguaglianza profonda per gli oggetti Error
. Solo la proprietà message
di un errore viene considerata per l'uguaglianza. Per personalizzare l'uguaglianza per controllare proprietà diverse da message
, utilizzare expect.addEqualityTesters
. Per verificare se qualcosa è stato lanciato, utilizzare l'asserzione toThrowError
.
toStrictEqual
- Tipo:
(received: any) => Awaitable<void>
toStrictEqual
verifica se un valore è uguale a un altro, o se ha la stessa struttura e tipo nel caso di oggetti (confronto ricorsivo).
Differenze da .toEqual
:
- Le chiavi con proprietà
undefined
vengono controllate. es.{a: undefined, b: 2}
non corrisponde a{b: 2}
quando si usa.toStrictEqual
. - Viene verificata la presenza di elementi vuoti nell'array. es.
[, 1]
non corrisponde a[undefined, 1]
quando si usa.toStrictEqual
. - I tipi di oggetto vengono controllati per essere uguali. es. 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 verificare se una stringa è una sottostringa di un'altra stringa. Da Vitest 1.0, se si eseguono test in un ambiente simile a un browser, questa asserzione può anche verificare se la classe è contenuta in una classList
o se un elemento è all'interno di un altro.
import { expect, test } from 'vitest';
import { getAllFruits } from './stocks.js';
test("l'elenco della frutta contiene orange", () => {
expect(getAllFruits()).toContain('orange');
const element = document.querySelector('#el');
// element ha una classe
expect(element.classList).toContain('flex');
// element è all'interno di un altro
expect(document.querySelector('#wrapper')).toContain(element);
});
toContainEqual
- Tipo:
(received: any) => Awaitable<void>
toContainEqual
verifica se un array contiene un elemento con una specifica struttura e valori. 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
verifica se un oggetto ha una proprietà .length
con un determinato 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
verifica se un oggetto possiede una proprietà con la chiave specificata (key
).
È possibile fornire un argomento valore opzionale per eseguire un confronto di uguaglianza profonda (deep equality), simile al matcher toEqual
, con il valore della proprietà.
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'); // Verifica che la proprietà esista
expect(invoice).toHaveProperty('total_amount', 5000); // Verifica che la chiave esista e che il valore corrisponda
expect(invoice).not.toHaveProperty('account'); // Verifica che la proprietà non esista
// Riferimento a proprietà annidate 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 a proprietà annidate usando un array contenente la chiave
expect(invoice).toHaveProperty('items[0].type', 'apples');
expect(invoice).toHaveProperty('items.0.type', 'apples'); // Anche la notazione a punti funziona
// Riferimento a proprietà annidate 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
// Includi la chiave in un array per evitare che venga interpretata come riferimento a proprietà annidata
expect(invoice).toHaveProperty(['P.O'], '12345');
});
toMatch
- Tipo:
(received: string | regexp) => Awaitable<void>
toMatch
verifica se una stringa corrisponde a un'espressione regolare.
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 stringhe
});
toMatchObject
- Tipo:
(received: object | array) => Awaitable<void>
toMatchObject
verifica se un oggetto corrisponde a un sottoinsieme delle proprietà di un altro oggetto.
È possibile fornire anche un array di oggetti. Questo è utile per verificare che due array abbiano lo stesso numero di elementi, a differenza di arrayContaining
che permette elementi aggiuntivi 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', () => {
// Verifica che un array di oggetti corrisponda
expect([{ foo: 'bar' }, { baz: 1 }]).toMatchObject([
{ foo: 'bar' },
{ baz: 1 },
]);
});
toThrowError
Tipo:
(received: any) => Awaitable<void>
Alias:
toThrow
toThrowError
verifica se una funzione genera un errore quando viene invocata.
È possibile fornire un argomento opzionale per verificare che venga generato un tipo di errore specifico:
- espressione regolare: il messaggio di errore deve corrispondere al pattern.
- stringa: il messaggio di errore deve includere la sottostringa.
TIP
È necessario racchiudere il codice in una funzione, altrimenti l'errore non verrà catturato e il test fallirà.
Ad esempio, per verificare che getFruitStock('pineapples')
generi un'eccezione, si può scrivere:
import { expect, test } from 'vitest';
function getFruitStock(type: string) {
if (type === 'pineapples') throw new Error('Pineapples are not in stock');
// Esegui altre operazioni
}
test('throws on pineapples', () => {
// Verifica che il messaggio di errore contenga "stock": questi sono equivalenti
expect(() => getFruitStock('pineapples')).toThrowError(/stock/);
expect(() => getFruitStock('pineapples')).toThrowError('stock');
// Verifica l'esatto messaggio di errore
expect(() => getFruitStock('pineapples')).toThrowError(
/^Pineapples are not in stock$/
);
});
TIP
Per testare funzioni asincrone, utilizzare 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, message?: string) => void
Questo metodo verifica che un valore corrisponda all'ultimo snapshot salvato.
È possibile fornire una stringa hint
opzionale che viene aggiunta al nome del test. Sebbene Vitest aggiunga sempre un numero alla fine del nome di uno snapshot, brevi suggerimenti descrittivi possono essere più utili dei numeri per differenziare più snapshot all'interno di un singolo blocco it
o test. Vitest ordina gli snapshot per nome nel file .snap
associato.
TIP
Quando lo snapshot non corrisponde e causa il fallimento del test, se la mancata corrispondenza è prevista, è possibile premere u
per aggiornare lo snapshot. In alternativa, è possibile passare le opzioni CLI -u
o --update
per fare in modo che Vitest aggiorni sempre gli snapshot.
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 una parte dell'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, message?: string) => void
Questo metodo verifica che un valore corrisponda all'ultimo snapshot salvato.
Vitest aggiunge e aggiorna l'argomento inlineSnapshot
(di tipo stringa) direttamente nel file di test, all'interno del matcher (invece di utilizzare 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 quando si aggiorna lo snapshot
expect(data).toMatchInlineSnapshot(`
{
"foo": Set {
"bar",
"snapshot",
},
}
`);
});
È anche possibile fornire una "forma" di un oggetto, se si sta testando solo una parte dell'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 0.30.0+
- Tipo:
<T>(filepath: string, message?: string) => Promise<void>
Confronta o aggiorna lo snapshot con il contenuto di un file specificato esplicitamente (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');
});
Poiché l'operazione di accesso al file system è asincrona, è necessario utilizzare await
con toMatchFileSnapshot()
.
toThrowErrorMatchingSnapshot
- Tipo:
(message?: string) => void
Funziona come toMatchSnapshot
, ma si aspetta lo stesso valore di toThrowError
.
toThrowErrorMatchingInlineSnapshot
- Tipo:
(snapshot?: string, message?: string) => void
Funziona come toMatchInlineSnapshot
, ma si aspetta lo stesso valore di toThrowError
.
toHaveBeenCalled
- Tipo:
() => Awaitable<void>
Questa asserzione è utile per verificare se una funzione è stata chiamata. Richiede di passare una spy function 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 verifica se una funzione è stata chiamata un numero specifico di volte. Richiede di passare una spy function 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 verifica se una funzione è stata chiamata almeno una volta con specifici parametri. Richiede di passare una spy function 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);
});
toHaveBeenLastCalledWith
- Tipo:
(...args: any[]) => Awaitable<void>
Questa asserzione verifica se una funzione è stata chiamata con specifici parametri durante la sua ultima invocazione. Richiede di passare una spy function 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 verifica se una funzione è stata chiamata con specifici parametri in una specifica invocazione. Il conteggio parte da 1. Quindi, per controllare la seconda invocazione, si scriverebbe .toHaveBeenNthCalledWith(2, ...)
.
Richiede di passare una spy function 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 verifica se una funzione ha restituito un valore con successo almeno una volta (ovvero, senza generare un errore). Richiede di passare una spy function 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 verifica se una funzione ha restituito un valore con successo un numero specifico di volte (ovvero, senza generare un errore). Richiede di passare una spy function 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>
Questa asserzione verifica se una funzione spia ha restituito un valore specifico almeno una volta. Richiede che una funzione spia sia passata a expect
.
import { expect, test, vi } from 'vitest';
test('la funzione spia restituisce un prodotto', () => {
const sell = vi.fn((product: string) => ({ product }));
sell('apples');
expect(sell).toHaveReturnedWith({ product: 'apples' });
});
toHaveLastReturnedWith
- Tipo:
(returnValue: any) => Awaitable<void>
Questa asserzione verifica se una funzione spia ha restituito un valore specifico all'ultima invocazione. Richiede che una funzione spia sia passata a expect
.
import { expect, test, vi } from 'vitest';
test("la funzione spia restituisce banane all'ultima chiamata", () => {
const sell = vi.fn((product: string) => ({ product }));
sell('apples');
sell('bananas');
expect(sell).toHaveLastReturnedWith({ product: 'bananas' });
});
toHaveNthReturnedWith
- Tipo:
(time: number, returnValue: any) => Awaitable<void>
Questa asserzione verifica se una funzione spia ha restituito un valore specifico alla n-esima chiamata. Richiede che una funzione spia sia passata a expect
.
import { expect, test, vi } from 'vitest';
test('la funzione spia restituisce banane alla seconda chiamata', () => {
const sell = vi.fn((product: string) => ({ product }));
sell('apples');
sell('bananas');
expect(sell).toHaveNthReturnedWith(2, { product: 'bananas' });
});
toSatisfy
- Tipo:
(predicate: (value: any) => boolean) => Awaitable<void>
Questa asserzione verifica se un valore soddisfa una condizione definita da un predicato.
import { describe, expect, it } from 'vitest';
describe('toSatisfy()', () => {
const isOdd = (value: number) => value % 2 !== 0;
it('passa con 1', () => {
expect(1).toSatisfy(isOdd);
});
it('passa con negato', () => {
expect(2).not.toSatisfy(isOdd);
});
});
resolves
- Tipo:
Promisify<Assertions>
resolves
semplifica l'asserzione di codice asincrono. Permette di estrarre il valore di risoluzione di una Promise e verificarlo con le asserzioni standard. Se la Promise viene rifiutata, l'asserzione fallisce.
Restituisce lo stesso oggetto Assertions
, ma tutti i matcher ora restituiscono Promise
, quindi è necessario utilizzare await
. Funziona anche con le asserzioni chai
.
Ad esempio, per una funzione che effettua una chiamata API e restituisce dei dati, si può asserire il valore di ritorno in questo modo:
import { expect, test } from 'vitest';
async function buyApples() {
return fetch('/buy/apples').then(r => r.json());
}
test('buyApples restituisce il nuovo ID stock', async () => {
// toEqual ora restituisce una promise, quindi DEVI usare await
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 con await
, si otterrà un test falso positivo che passerà sempre. Per assicurarsi che le asserzioni vengano effettivamente eseguite, si può utilizzare expect.assertions(number)
.
rejects
- Tipo:
Promisify<Assertions>
rejects
semplifica l'asserzione di codice asincrono. Permette di estrarre il motivo del rifiuto di una Promise e verificarlo con le asserzioni standard. Se la Promise viene risolta con successo, l'asserzione fallisce.
Restituisce lo stesso oggetto Assertions
, ma tutti i matcher ora restituiscono Promise
, quindi è necessario utilizzare await
. Funziona anche con le asserzioni chai
.
Ad esempio, per una funzione che genera un errore, si può asserire il motivo del rifiuto in questo modo:
import { expect, test } from 'vitest';
async function buyApples(id) {
if (!id) throw new Error('no id');
}
test('buyApples genera un errore quando non viene fornito alcun ID', async () => {
// toThrow ora restituisce una promise, quindi DEVI usare await
await expect(buyApples()).rejects.toThrow('no id');
});
WARNING
Se l'asserzione non viene attesa con await
, si otterrà un test falso positivo che passerà sempre. Per assicurarsi che le asserzioni vengano effettivamente eseguite, si può utilizzare expect.assertions(number)
.
expect.assertions
- Tipo:
(count: number) => void
Verifica che un certo numero di asserzioni sia stato eseguito durante un test, dopo che il test è stato completato (con successo o fallimento). Questo è utile per verificare se è stato eseguito codice asincrono.
Ad esempio, se una funzione chiama asincronamente due matcher, si può 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('tutte le asserzioni vengono chiamate', async () => {
expect.assertions(2);
function callback1(data) {
expect(data).toBeTruthy();
}
function callback2(data) {
expect(data).toBeTruthy();
}
await doAsync(callback1, callback2);
});
WARNING
Quando si utilizzano assertions
con test asincroni concorrenti, è necessario utilizzare expect
dal Contesto di test locale per garantire che venga rilevato il test corretto.
expect.hasAssertions
- Tipo:
() => void
Verifica che sia stata eseguita almeno un'asserzione durante un test, dopo che il test è stato completato (con successo o fallimento). Questo è utile per verificare se è stato eseguito codice asincrono.
Ad esempio, se un codice chiama una callback, si può fare un'asserzione all'interno della callback, ma il test passerà sempre se non si verifica se è stata chiamata un'asserzione.
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('la callback è stata chiamata', 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 indicare che una riga di codice non dovrebbe mai essere raggiunta. Se viene raggiunta, il test fallisce.
Ad esempio, per testare che build()
generi un'eccezione a causa della ricezione di directory senza una cartella src
e gestire anche ogni errore separatamente, si potrebbe fare questo:
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 fallisce con "%s"', async dir => {
try {
await build(dir);
expect.unreachable('Non avrebbe dovuto superare la 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('Tutti i casi di errore devono essere gestiti');
break;
}
}
});
expect.anything
- Tipo:
() => any
Questo matcher asimmetrico, se utilizzato in un controllo di uguaglianza, restituirà sempre true
. È utile quando si vuole solo verificare che una proprietà esista.
import { expect, test } from 'vitest';
test("l'oggetto contiene la chiave 'apples'", () => {
expect({ apples: 22 }).toEqual({ apples: expect.anything() });
});
expect.any
- Tipo:
(constructor: unknown) => any
Questo matcher asimmetrico, se utilizzato in un controllo di uguaglianza, restituirà true
solo se il valore è un'istanza del costruttore specificato. È utile quando si ha un valore che viene generato dinamicamente e si vuole solo verificare che sia del tipo corretto.
import { expect, test } from 'vitest';
import { generateId } from './generators.js';
test('"id" è un numero', () => {
expect({ id: generateId() }).toEqual({ id: expect.any(Number) });
});
expect.closeTo 1.0.0+
- Tipo:
(expected: any, precision?: number) => any
expect.closeTo
è utile quando si confrontano numeri in virgola mobile nelle proprietà degli oggetti o negli elementi dell'array. Se devi confrontare un numero, utilizza invece .toBeCloseTo
.
L'argomento opzionale numDigits
limita il numero di cifre da controllare dopo la virgola decimale. Per il valore predefinito 2
, il criterio di test è Math.abs(expected - received) < 0.005 (ovvero, 10 ** -2 / 2)
.
Ad esempio, questo test viene superato con una precisione di 5 cifre:
test('confronta float nelle proprietà degli oggetti', () => {
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 in un controllo di uguaglianza, questo matcher asimmetrico restituirà true
se il valore è un array e contiene tutti gli elementi specificati.
import { expect, test } from 'vitest';
test('il cesto contiene la varietà Fuji', () => {
const basket = {
varieties: ['Empire', 'Fuji', 'Gala'],
count: 3,
};
expect(basket).toEqual({
count: 3,
varieties: expect.arrayContaining(['Fuji']),
});
});
TIP
È possibile utilizzare expect.not
con questo matcher per negare il valore previsto.
expect.objectContaining
- Tipo:
(expected: any) => any
Se utilizzato in un controllo di uguaglianza, questo matcher asimmetrico restituirà true
se il valore è un oggetto e contiene almeno le proprietà specificate con i valori corrispondenti.
import { expect, test } from 'vitest';
test('il cesto ha mele empire', () => {
const basket = {
varieties: [
{
name: 'Empire',
count: 1,
},
],
};
expect(basket).toEqual({
varieties: [expect.objectContaining({ name: 'Empire' })],
});
});
TIP
È possibile utilizzare expect.not
con questo matcher per negare il valore previsto.
expect.stringContaining
- Tipo:
(expected: any) => any
Se utilizzato in un controllo di uguaglianza, questo matcher asimmetrico restituirà true
se il valore è una stringa e contiene la sottostringa specificata.
import { expect, test } from 'vitest';
test("il nome della varietà contiene 'Emp'", () => {
const variety = {
name: 'Empire',
count: 1,
};
expect(variety).toEqual({
name: expect.stringContaining('Emp'),
count: 1,
});
});
TIP
È possibile utilizzare expect.not
con questo matcher per negare il valore previsto.
expect.stringMatching
- Tipo:
(expected: any) => any
Se utilizzato in un controllo di uguaglianza, questo matcher asimmetrico restituirà true
se il valore è una stringa e corrisponde all'espressione regolare specificata.
import { expect, test } from 'vitest';
test("il nome della varietà termina con 're'", () => {
const variety = {
name: 'Empire',
count: 1,
};
expect(variety).toEqual({
name: expect.stringMatching(/re$/),
count: 1,
});
});
TIP
È possibile utilizzare expect.not
con questo matcher per negare il valore previsto.
expect.addSnapshotSerializer
- Tipo:
(plugin: PrettyFormatPlugin) => void
Questo metodo aggiunge serializzatori personalizzati che vengono utilizzati durante la creazione di uno snapshot. Questa è una funzionalità avanzata: per maggiori informazioni, consultare la guida sui serializzatori personalizzati.
Se si aggiungono serializzatori personalizzati, è necessario chiamare questo metodo all'interno di setupFiles
. Questo influirà su ogni snapshot.
TIP
Se in precedenza si utilizzava Vue CLI con Jest, si potrebbe voler installare jest-serializer-vue. Altrimenti, gli snapshot verranno racchiusi in una stringa, il che farà sì che "
venga sottoposto a escape.
expect.extend
- Tipo:
(matchers: MatchersObject) => void
Permette di estendere i matcher predefiniti con matcher personalizzati. Questa funzione viene utilizzata per estendere l'oggetto matcher con nuovi matcher.
Quando si definiscono i matcher in questo modo, si creano anche dei matcher asimmetrici che possono essere utilizzati come expect.stringContaining
.
import { expect, test } from 'vitest';
test('matcher personalizzati', () => {
expect.extend({
toBeFoo: (received, expected) => {
if (received !== 'foo') {
return {
message: () => `ci si aspettava che ${received} fosse foo`,
pass: false,
};
}
},
});
expect('foo').toBeFoo();
expect({ foo: 'foo' }).toEqual({ foo: expect.toBeFoo() });
});
TIP
Se si vuole che i matcher personalizzati siano disponibili in ogni test, è necessario chiamare questo metodo all'interno di setupFiles
.
Questa funzione mantiene la compatibilità con expect.extend
di Jest, quindi qualsiasi libreria che la utilizza per creare matcher personalizzati funzionerà con Vitest.
Se si utilizza TypeScript, a partire da Vitest 0.31.0 è possibile estendere l'interfaccia Assertion
predefinita in un file di dichiarazione ambient (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 ambient nel tuo tsconfig.json
.
TIP
Per maggiori informazioni, consultare la guida sull'estensione dei matcher.
expect.addEqualityTesters 1.2.0+
- Tipo:
(tester: Array<Tester>) => void
Puoi utilizzare questo metodo per definire tester personalizzati, che sono metodi utilizzati dai matcher per verificare se due oggetti sono uguali. È compatibile con 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 = isBAnagramComparator(b);
if (isAAnagramComparator && isBAnagramComparator) return a.equals(b);
else if (isAAnagramComparator === isBAnagramComparator) return undefined;
else return false;
}
expect.addEqualityTesters([areAnagramsEqual]);
test('tester di uguaglianza personalizzato', () => {
expect(new AnagramComparator('listen')).toEqual(
new AnagramComparator('silent')
);
});