expect
В приведенных ниже сигнатурах типов используются следующие типы:
type Awaitable<T> = T | PromiseLike<T>;
expect
используется для создания утверждений. В данном контексте, утверждения
— это функции, которые проверяют, выполняется ли определенное условие. Vitest по умолчанию предоставляет утверждения chai
, а также утверждения, совместимые с Jest
, основанные на chai
.
Например, этот код утверждает, что значение input
равно 2
. Если это не так, утверждение вызовет ошибку, и тест завершится неудачей.
import { expect } from 'vitest';
const input = Math.sqrt(4);
expect(input).to.equal(2); // chai API
expect(input).toBe(2); // jest API
Технически, этот пример не использует функцию test
, поэтому в консоли вы увидите ошибку Node.js вместо вывода Vitest. Чтобы узнать больше о test
, обратитесь к Test API Reference.
Кроме того, expect
можно использовать статически для доступа к функциям сопоставления, описанным далее, и другим возможностям.
WARNING
expect
не влияет на проверку типов, если в выражении нет ошибки типа. Если вы хотите использовать Vitest для проверки типов, используйте expectTypeOf
или assertType
.
soft
- Тип:
ExpectStatic & (actual: any) => Assertions
expect.soft
работает аналогично expect
, но вместо того, чтобы завершать выполнение теста при неудачном утверждении, он продолжает выполняться и помечает неудачу как сбой теста. Все ошибки, возникшие во время теста, будут показаны после его завершения.
import { expect, test } from 'vitest';
test('expect.soft test', () => {
expect.soft(1 + 1).toBe(3); // помечает тест как провалившийся и продолжает выполнение
expect.soft(1 + 2).toBe(4); // помечает тест как провалившийся и продолжает выполнение
});
// В конце теста будут выведены вышеуказанные ошибки.
Его также можно использовать вместе с expect
. Если утверждение expect
завершается неудачей, тест будет завершен, и все ошибки будут отображены.
import { expect, test } from 'vitest';
test('expect.soft test', () => {
expect.soft(1 + 1).toBe(3); // помечает тест как провалившийся и продолжает выполнение
expect(1 + 2).toBe(3); // провалился и завершил тест, все предыдущие ошибки будут выведены
expect.soft(1 + 2).toBe(4); // не выполняется
});
WARNING
expect.soft
можно использовать только внутри функции test
.
not
Использование not
инвертирует утверждение. Например, этот код утверждает, что значение input
не равно 2
. Если оно равно, утверждение вызовет ошибку, и тест завершится неудачей.
import { expect, test } from 'vitest';
const input = Math.sqrt(16);
expect(input).not.to.equal(2); // chai API
expect(input).not.toBe(2); // jest API
toBe
Тип:
(value: any) => Awaitable<void>
toBe
можно использовать для утверждения, равны ли примитивы, или что объекты имеют одну и ту же ссылку. Это аналогично вызовуexpect(Object.is(3, 3)).toBe(true)
. Если объекты не совпадают, но вы хотите проверить, идентичны ли их структуры, используйтеtoEqual
.Например, код ниже проверяет, есть ли у трейдера 13 яблок.
tsimport { 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; // same reference expect(stock).toBe(refStock); });
Старайтесь не использовать
toBe
с числами с плавающей точкой. Поскольку JavaScript округляет их,0.1 + 0.2
не является строго0.3
. Чтобы надежно утверждать числа с плавающей точкой, используйте утверждениеtoBeCloseTo
.
toBeCloseTo
Тип:
(value: number, numDigits?: number) => Awaitable<void>
Используйте
toBeCloseTo
для сравнения чисел с плавающей точкой. Необязательный аргументnumDigits
указывает, сколько знаков после запятой следует учитывать при сравнении. Например:tsimport { expect, test } from 'vitest'; test.fails('decimals are not equal in javascript', () => { expect(0.2 + 0.1).toBe(0.3); // 0.2 + 0.1 is 0.30000000000000004 }); test('decimals are rounded to 5 after the point', () => { // 0.2 + 0.1 is 0.30000 | "000000000004" removed expect(0.2 + 0.1).toBeCloseTo(0.3, 5); // nothing from 0.30000000000000004 is removed expect(0.2 + 0.1).not.toBeCloseTo(0.3, 50); });
toBeDefined
Тип:
() => Awaitable<void>
toBeDefined
проверяет, что значение не равноundefined
. Полезно для проверки, возвращает ли функция какое-либо значение.tsimport { expect, test } from 'vitest'; function getApples() { return 3; } test('function returned something', () => { expect(getApples()).toBeDefined(); });
toBeUndefined
Тип:
() => Awaitable<void>
Обратное утверждение
toBeDefined
,toBeUndefined
проверяет, что значение равноundefined
. Полезно для проверки, не возвращает ли функция какое-либо значение.tsimport { expect, test } from 'vitest'; function getApplesFromStock(stock) { if (stock === 'Bill') return 13; } test("mary doesn't have a stock", () => { expect(getApplesFromStock('Mary')).toBeUndefined(); });
toBeTruthy
Тип:
() => Awaitable<void>
toBeTruthy
проверяет, что значение истинно при преобразовании в boolean. Полезно, когда важен не сам результат, а возможность его приведения кtrue
.Например, имея этот код, вам не важно возвращаемое значение
stocks.getInfo
- это может быть сложный объект, строка или что-то еще. Код все равно будет работать.tsimport { Stocks } from './stocks.js'; const stocks = new Stocks(); stocks.sync('Bill'); if (stocks.getInfo('Bill')) stocks.sell('apples', 'Bill');
Итак, если вы хотите проверить, что
stocks.getInfo
будет истинным, вы можете написать:tsimport { 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(); });
В JavaScript любое значение считается истинным, кроме
false
,0
,''
,null
,undefined
иNaN
.
toBeFalsy
Тип:
() => Awaitable<void>
toBeFalsy
проверяет, что значение ложно при преобразовании в boolean. Полезно, когда важен не сам результат, а возможность его приведения кfalse
.Например, имея этот код, вам не важно возвращаемое значение
stocks.stockFailed
- он может вернуть любое ложное значение, но код все равно будет работать.tsimport { Stocks } from './stocks.js'; const stocks = new Stocks(); stocks.sync('Bill'); if (!stocks.stockFailed('Bill')) stocks.sell('apples', 'Bill');
Итак, если вы хотите проверить, что
stocks.stockFailed
будет ложным, вы можете написать:tsimport { 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(); });
В JavaScript любое значение считается истинным, кроме
false
,0
,''
,null
,undefined
иNaN
.
toBeNull
Тип:
() => Awaitable<void>
toBeNull
просто проверяет, является ли что-тоnull
. Это псевдоним для.toBe(null)
.tsimport { expect, test } from 'vitest'; function apples() { return null; } test("we don't have apples", () => { expect(apples()).toBeNull(); });
toBeNaN
Тип:
() => Awaitable<void>
toBeNaN
просто проверяет, является ли что-тоNaN
. Это псевдоним для.toBe(NaN)
.tsimport { 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
Тип:
(c: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => Awaitable<void>
toBeTypeOf
проверяет, имеет ли фактическое значение тип, соответствующий полученному типу.tsimport { expect, test } from 'vitest'; const actual = 'stock'; test('stock is type of string', () => { expect(actual).toBeTypeOf('string'); });
toBeInstanceOf
Тип:
(c: any) => Awaitable<void>
toBeInstanceOf
проверяет, является ли фактическое значение экземпляром полученного класса.tsimport { expect, test } from 'vitest'; import { Stocks } from './stocks.js'; const stocks = new Stocks(); test('stocks are instance of Stocks', () => { expect(stocks).toBeInstanceOf(Stocks); });
toBeGreaterThan
Тип:
(n: number | bigint) => Awaitable<void>
toBeGreaterThan
проверяет, что фактическое значение больше полученного. Равные значения приведут к сбою теста.tsimport { expect, test } from 'vitest'; import { getApples } from './stocks.js'; test('have more then 10 apples', () => { expect(getApples()).toBeGreaterThan(10); });
toBeGreaterThanOrEqual
Тип:
(n: number | bigint) => Awaitable<void>
toBeGreaterThanOrEqual
проверяет, что фактическое значение больше полученного или равно ему.tsimport { expect, test } from 'vitest'; import { getApples } from './stocks.js'; test('have 11 apples or more', () => { expect(getApples()).toBeGreaterThanOrEqual(11); });
toBeLessThan
Тип:
(n: number | bigint) => Awaitable<void>
toBeLessThan
проверяет, что фактическое значение меньше полученного. Равные значения приведут к сбою теста.tsimport { expect, test } from 'vitest'; import { getApples } from './stocks.js'; test('have less then 20 apples', () => { expect(getApples()).toBeLessThan(20); });
toBeLessThanOrEqual
Тип:
(n: number | bigint) => Awaitable<void>
toBeLessThanOrEqual
проверяет, что фактическое значение меньше полученного или равно ему.tsimport { expect, test } from 'vitest'; import { getApples } from './stocks.js'; test('have 11 apples or less', () => { expect(getApples()).toBeLessThanOrEqual(11); });
toEqual
Тип:
(received: any) => Awaitable<void>
toEqual
проверяет, что фактическое значение равно полученному или имеет ту же структуру, если это объект (сравнивает их рекурсивно). Разница междуtoEqual
иtoBe
показана в следующем примере:tsimport { 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
Глубокое сравнение на равенство не будет выполняться для объектов
Error
. Чтобы проверить, было ли что-то выброшено, используйте утверждениеtoThrowError
.
toStrictEqual
Тип:
(received: any) => Awaitable<void>
toStrictEqual
проверяет, что фактическое значение равно полученному или имеет ту же структуру, если это объект (сравнивает их рекурсивно), и того же типа.Отличия от
.toEqual
:- Проверяются ключи со свойствами
undefined
. Например,{a: undefined, b: 2}
не соответствует{b: 2}
при использовании.toStrictEqual
. - Проверяется наличие пропусков в массиве. Например,
[, 1]
не соответствует[undefined, 1]
при использовании.toStrictEqual
. - Проверяется идентичность типов объектов. Например, экземпляр класса с полями
a
иb
не будет эквивалентен литеральному объекту с полямиa
иb
.
tsimport { 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
Тип:
(received: string) => Awaitable<void>
toContain
проверяет, содержится ли фактическое значение в массиве.toContain
также может проверить, является ли строка подстрокой другой строки.tsimport { expect, test } from 'vitest'; import { getAllFruits } from './stocks.js'; test('the fruit list contains orange', () => { expect(getAllFruits()).toContain('orange'); });
toContainEqual
Тип:
(received: any) => Awaitable<void>
toContainEqual
проверяет, что элемент с определенной структурой и значениями содержится в массиве. Он работает какtoEqual
внутри для каждого элемента.tsimport { expect, test } from 'vitest'; import { getFruitStock } from './stocks.js'; test('apple available', () => { expect(getFruitStock()).toContainEqual({ fruit: 'apple', count: 5 }); });
toHaveLength
Тип:
(received: number) => Awaitable<void>
toHaveLength
проверяет, имеет ли объект свойство.length
с определенным числовым значением.tsimport { expect, test } from 'vitest'; test('toHaveLength', () => { expect('abc').toHaveLength(3); expect([1, 2, 3]).toHaveLength(3); expect('').not.toHaveLength(3); // не имеет .length равным 3 expect({ length: 3 }).toHaveLength(3); });
toHaveProperty
Type:
(key: any, received?: any) => Awaitable<void>
toHaveProperty
проверяет наличие свойства с указанным ключомkey
в объекте.Вы можете также передать необязательный аргумент
value
для сравнения значения свойства с ожидаемым значением (глубокое равенство, как вtoEqual
).tsimport { 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'); // Проверяет, что ключ существует expect(invoice).toHaveProperty('total_amount', 5000); // Проверяет, что ключ существует и значение равно 5000 expect(invoice).not.toHaveProperty('account'); // Проверяет, что ключ не существует // Доступ к вложенным свойствам через точечную нотацию expect(invoice).toHaveProperty('customer.first_name'); expect(invoice).toHaveProperty('customer.last_name', 'Doe'); expect(invoice).not.toHaveProperty('customer.location', 'India'); // Доступ к вложенным свойствам через массив, содержащий ключ expect(invoice).toHaveProperty('items[0].type', 'apples'); expect(invoice).toHaveProperty('items.0.type', 'apples'); // Точечная нотация также работает // Доступ к вложенным свойствам через массив, содержащий путь к ключу expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples'); expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples'); // Строковая нотация также работает // Заключите ключ в массив, чтобы избежать его интерпретации как пути к вложенному свойству expect(invoice).toHaveProperty(['P.O'], '12345'); });
toMatch
Type:
(received: string | regexp) => Awaitable<void>
toMatch
проверяет, соответствует ли строка регулярному выражению или другой строке.tsimport { expect, test } from 'vitest'; test('top fruits', () => { expect('top fruits include apple, orange and grape').toMatch(/apple/); expect('applefruits').toMatch('fruit'); // toMatch также принимает строку });
TIP
Если сообщение об ошибке слишком короткое, вы можете увеличить значение параметра chaiConfig.truncateThreshold
в вашем файле конфигурации: chaiConfig.truncateThreshold.
toMatchObject
Type:
(received: object | array) => Awaitable<void>
toMatchObject
проверяет, содержит ли объект подмножество свойств другого объекта.Вы также можете передать массив объектов. Это полезно, если вы хотите проверить, что два массива содержат одинаковое количество элементов и соответствующие элементы совпадают, в отличие от
arrayContaining
, который допускает наличие дополнительных элементов в проверяемом массиве.tsimport { 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', () => { // Проверяет, что массивы объектов совпадают expect([{ foo: 'bar' }, { baz: 1 }]).toMatchObject([ { foo: 'bar' }, { baz: 1 }, ]); });
toThrowError
Type:
(received: any) => Awaitable<void>
Alias:
toThrow
toThrowError
проверяет, выбрасывает ли функция ошибку при вызове.Вы можете передать необязательный аргумент для проверки типа выбрасываемой ошибки:
- regular expression: сообщение об ошибке соответствует регулярному выражению
- string: сообщение об ошибке содержит указанную подстроку
TIP
Обязательно оберните код в функцию, иначе ошибка не будет перехвачена, и тест завершится неудачей.
Например, чтобы проверить, что
getFruitStock('pineapples')
выбрасывает ошибку, можно написать:tsimport { expect, test } from 'vitest'; function getFruitStock(type) { if (type === 'pineapples') throw new DiabetesError( 'Pineapples are not good for people with diabetes' ); // Выполнение других действий } test('throws on pineapples', () => { // Проверяет, что сообщение об ошибке содержит "diabetes": эти записи эквивалентны expect(() => getFruitStock('pineapples')).toThrowError(/diabetes/); expect(() => getFruitStock('pineapples')).toThrowError('diabetes'); // Проверяет точное сообщение об ошибке expect(() => getFruitStock('pineapples')).toThrowError( /^Pineapples are not good for people with diabetes$/ ); });
TIP
Для тестирования асинхронных функций используйте в сочетании с rejects.
jsfunction getAsyncFruitStock() { return Promise.reject(new Error('empty')); } test('throws on pineapples', async () => { await expect(() => getAsyncFruitStock()).rejects.toThrowError('empty'); });
toMatchSnapshot
Type:
<T>(shape?: Partial<T> | string, message?: string) => void
Проверяет, что значение соответствует сохраненному снимку (snapshot).
Вы можете передать необязательный строковый аргумент
hint
, который будет добавлен к имени теста. Vitest всегда добавляет число в конце имени снимка, но короткие описательные подсказки могут быть полезнее чисел для различения нескольких снимков в одном блокеit
илиtest
. Vitest сортирует снимки по имени в соответствующем файле.snap
.TIP
Когда снимок не совпадает и тест завершается с ошибкой, вы можете нажать клавишу
u
для однократного обновления снимка. Или вы можете использовать параметры командной строки-u
или--update
, чтобы Vitest всегда обновлял снимки.tsimport { expect, test } from 'vitest'; test('matches snapshot', () => { const data = { foo: new Set(['bar', 'snapshot']) }; expect(data).toMatchSnapshot(); });
Вы также можете предоставить структуру объекта (shape of an object), если вы тестируете только структуру объекта и не требуете 100% соответствия данным:
tsimport { expect, test } from 'vitest'; test('matches snapshot', () => { const data = { foo: new Set(['bar', 'snapshot']) }; expect(data).toMatchSnapshot({ foo: expect.any(Set) }); });
toMatchInlineSnapshot
Type:
<T>(shape?: Partial<T> | string, snapshot?: string, message?: string) => void
Проверяет, что значение соответствует встроенному снимку (inline snapshot).
Vitest добавляет и обновляет строковый аргумент
inlineSnapshot
для матчера непосредственно в файле теста (вместо внешнего файла.snap
).tsimport { expect, test } from 'vitest'; test('matches inline snapshot', () => { const data = { foo: new Set(['bar', 'snapshot']) }; // Vitest обновит следующее содержимое при обновлении моментального снимка expect(data).toMatchInlineSnapshot(` { "foo": Set { "bar", "snapshot", }, } `); });
Вы также можете предоставить структуру объекта, если вы тестируете только структуру объекта и не требуете 100% соответствия данным:
tsimport { 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
Type:
<T>(filepath: string, message?: string) => Promise<void>
Version: Since Vitest 0.30.0
Сравнивает или обновляет снимок с содержимым файла, указанного явно (вместо файла
.snap
).tsimport { expect, it } from 'vitest'; it('render basic', async () => { const result = renderHTML(h('div', { class: 'foo' })); await expect(result).toMatchFileSnapshot('./test/basic.output.html'); });
Обратите внимание, что поскольку операция с файловой системой является асинхронной, необходимо использовать
await
сtoMatchFileSnapshot()
.
toThrowErrorMatchingSnapshot
Type:
(message?: string) => void
Аналогичен
toMatchSnapshot
, но применяется к значению, которое, как ожидается, будет выброшено функцией (как вtoThrowError
).Если функция выбрасывает
Error
, снимком будет сообщение об ошибке. В противном случае снимком будет значение, выброшенное функцией.
toThrowErrorMatchingInlineSnapshot
Type:
(snapshot?: string, message?: string) => void
Аналогичен
toMatchInlineSnapshot
, но применяется к значению, которое, как ожидается, будет выброшено функцией (как вtoThrowError
).Если функция выбрасывает
Error
, снимком будет сообщение об ошибке. В противном случае снимком будет значение, выброшенное функцией.
toHaveBeenCalled
Type:
() => Awaitable<void>
Проверяет, была ли вызвана функция. Требует, чтобы в
expect
была передана шпионская функция (spy function).tsimport { 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
- Type:
(amount: number) => Awaitable<void>
Проверяет, что функция была вызвана указанное количество раз. Требует, чтобы в 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
- Type:
(...args: any[]) => Awaitable<void>
Проверяет, что функция была вызвана хотя бы один раз с указанными аргументами. Требует, чтобы в 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
- Type:
(...args: any[]) => Awaitable<void>
Проверяет, что при последнем вызове функция была вызвана с указанными аргументами. Требует, чтобы в 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
- Type:
(time: number, ...args: any[]) => Awaitable<void>
Проверяет, была ли функция вызвана с определенными аргументами при конкретном вызове (нумерация начинается с 1). Например, чтобы проверить второй вызов, используйте .toHaveBeenNthCalledWith(2, ...)
.
Требует, чтобы в 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
- Type:
() => Awaitable<void>
Проверяет, вернула ли функция какое-либо значение хотя бы один раз (успешно, без выброса ошибки). Требует, чтобы в 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
- Type:
(amount: number) => Awaitable<void>
Проверяет, что функция успешно вернула значение указанное количество раз (т.е. не выбрасывала ошибку). Требует, чтобы в 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
- Тип:
(returnValue: any) => Awaitable<void>
Вы можете использовать это утверждение, чтобы проверить, возвращала ли функция значение, соответствующее указанному, хотя бы один раз. Шпион (spy function) должен быть передан в 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
- Тип:
(returnValue: any) => Awaitable<void>
Вы можете использовать это утверждение, чтобы проверить, возвращала ли функция значение, соответствующее указанному, при последнем вызове. Шпион (spy function) должен быть передан в 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
- Тип:
(time: number, returnValue: any) => Awaitable<void>
Вы можете использовать это утверждение, чтобы проверить, возвращала ли функция значение, соответствующее указанному, при определенном вызове (например, при втором вызове). Шпион (spy function) должен быть передан в 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' });
});
toSatisfy
- Тип:
(predicate: (value: any) => boolean) => Awaitable<void>
Это утверждение проверяет, удовлетворяет ли значение заданному предикату (функции, возвращающей true
или false
).
describe('toSatisfy()', () => {
const isOdd = (value: number) => value % 2 !== 0;
it('pass with 0', () => {
expect(1).toSatisfy(isOdd);
});
it('pass with negotiation', () => {
expect(2).not.toSatisfy(isOdd);
});
});
resolves
Тип:
Promisify<Assertions>
resolves
упрощает утверждения для асинхронного кода. Используйте его, чтобы получить значение из разрешенного промиса и проверить его с помощью обычных утверждений. Если промис отклоняется, утверждение завершится с ошибкой.Он возвращает тот же объект
Assertions
, но теперь все матчеры возвращаютPromise
, поэтому необходимо использоватьawait
. Также работает с утверждениямиchai
.Например, если у вас есть функция, которая выполняет вызов API и возвращает данные, вы можете использовать следующий код для проверки возвращаемого значения:
tsimport { expect, test } from 'vitest'; async function buyApples() { return fetch('/buy/apples').then(r => r.json()); } test('buyApples returns new stock id', async () => { // toEqual теперь возвращает промис, поэтому вы ДОЛЖНЫ дождаться его await expect(buyApples()).resolves.toEqual({ id: 1 }); // jest API await expect(buyApples()).resolves.to.equal({ id: 1 }); // chai API });
WARNING
Если не дождаться выполнения утверждения, тест может дать ложноположительный результат. Чтобы убедиться, что утверждения действительно вызываются, используйте
expect.assertions(number)
.
rejects
Тип:
Promisify<Assertions>
rejects
упрощает утверждения для асинхронного кода. Используйте его, чтобы получить причину отклонения промиса и проверить ее с помощью обычных утверждений. Если промис успешно разрешается, утверждение завершится с ошибкой.Он возвращает тот же объект
Assertions
, но теперь все матчеры возвращаютPromise
, поэтому необходимо использоватьawait
. Также работает с утверждениямиchai
.Например, если у вас есть функция, которая завершается с ошибкой при вызове, вы можете использовать следующий код для проверки причины ошибки:
tsimport { 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 теперь возвращает промис, поэтому вы ДОЛЖНЫ дождаться его await expect(buyApples()).rejects.toThrow('no id'); });
WARNING
Если не дождаться выполнения утверждения, тест может дать ложноположительный результат. Чтобы убедиться, что утверждения действительно вызываются, используйте
expect.assertions(number)
.
expect.assertions
Тип:
(count: number) => void
Проверяет, было ли вызвано указанное количество утверждений после завершения теста (независимо от того, пройден он или нет). Это полезно для проверки того, был ли вызван асинхронный код.
Например, если у нас есть функция, которая асинхронно вызывает два матчера, мы можем убедиться, что они действительно были вызваны.
tsimport { 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
При использовании
assertions
с асинхронными конкурентными тестами необходимо использоватьexpect
из локального Контекста теста, чтобы убедиться, что обнаружен правильный тест.
expect.hasAssertions
Тип:
() => void
Проверяет, было ли вызвано хотя бы одно утверждение после завершения теста (независимо от того, пройден он или нет). Это полезно для проверки того, был ли вызван асинхронный код.
Например, если у вас есть код, который вызывает обратный вызов, мы можем сделать утверждение внутри обратного вызова, но тест всегда будет проходить, если мы не проверим, было ли вызвано утверждение.
tsimport { expect, test } from 'vitest'; import { db } from './db.js'; const cbs = []; function onSelect(cb) { cbs.push(cb); } // после выбора из базы данных мы вызываем все обратные вызовы 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 => { // должен быть вызван при выборе expect(data).toBeTruthy(); }); // если не ожидать, тест завершится неудачей // если у вас нет expect.hasAssertions(), тест пройдет await select(3); });
expect.unreachable
Тип:
(message?: string) => never
Этот метод используется для указания, что определенная строка кода никогда не должна быть достигнута. Если она будет достигнута, тест завершится с ошибкой.
Например, если мы хотим проверить, что
build()
выдает исключение из-за отсутствия папкиsrc
в указанных каталогах, и обрабатывать каждую ошибку отдельно, мы можем сделать это:tsimport { 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: // для обработки всех возможных ошибок expect.unreachable('All error test must be handled'); break; } } });
expect.anything
Тип:
() => any
Этот асимметричный матчер всегда возвращает
true
при использовании в проверке на равенство. Полезно, если вам просто нужно убедиться, что свойство существует.tsimport { expect, test } from 'vitest'; test('object has "apples" key', () => { expect({ apples: 22 }).toEqual({ apples: expect.anything() }); });
expect.any
Тип:
(constructor: unknown) => any
Этот асимметричный матчер возвращает
true
при использовании в проверке на равенство, только если значение является экземпляром указанного конструктора. Полезно, если у вас есть значение, которое генерируется каждый раз, и вы хотите проверить только его тип.tsimport { expect, test } from 'vitest'; import { generateId } from './generators.js'; test('"id" is a number', () => { expect({ id: generateId() }).toEqual({ id: expect.any(Number) }); });
expect.arrayContaining
Тип:
<T>(expected: T[]) => any
При использовании в проверке на равенство, этот асимметричный матчер возвращает
true
, если значение является массивом и содержит указанные элементы.tsimport { 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
Вы можете использовать
expect.not
с этим матчером, чтобы инвертировать ожидаемое значение.
expect.objectContaining
Тип:
(expected: any) => any
При использовании в проверке на равенство, этот асимметричный матчер возвращает
true
, если значение имеет аналогичную структуру (содержит указанные свойства).tsimport { 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
Вы можете использовать
expect.not
с этим матчером, чтобы инвертировать ожидаемое значение.
expect.stringContaining
Тип:
(expected: any) => any
При использовании в проверке на равенство, этот асимметричный матчер возвращает
true
, если значение является строкой и содержит указанную подстроку.tsimport { 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
Вы можете использовать
expect.not
с этим матчером, чтобы инвертировать ожидаемое значение.
expect.stringMatching
Тип:
(expected: any) => any
При использовании в проверке на равенство, этот асимметричный матчер возвращает
true
, если значение является строкой и содержит указанную подстроку или соответствует регулярному выражению.tsimport { 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
Вы можете использовать
expect.not
с этим матчером, чтобы инвертировать ожидаемое значение.
expect.addSnapshotSerializer
Тип:
(plugin: PrettyFormatPlugin) => void
Этот метод добавляет пользовательские сериализаторы, которые вызываются при создании снимка (snapshot). Это продвинутая функциональность. Для получения дополнительной информации ознакомьтесь с руководством по пользовательским сериализаторам.
Если вы добавляете пользовательские сериализаторы, вы должны вызвать этот метод внутри
setupFiles
. Это повлияет на каждый снимок.TIP
Если вы ранее использовали Vue CLI с Jest, вы можете установить jest-serializer-vue. В противном случае ваши снимки будут обернуты в строку, что приведет к экранированию
"
.
expect.extend
Тип:
(matchers: MatchersObject) => void
Вы можете расширить стандартные матчеры своими собственными. Эта функция используется для добавления пользовательских матчеров в объект матчеров.
При определении матчеров таким образом, вы также создаете асимметричные матчеры, которые можно использовать, например,
expect.stringContaining
.tsimport { 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
Если вы хотите, чтобы ваши матчеры были доступны в каждом тесте, вы должны вызвать этот метод внутри
setupFiles
.Эта функция совместима с
expect.extend
Jest, поэтому любая библиотека, использующая ее для создания пользовательских матчеров, будет работать с Vitest.Если вы используете TypeScript, начиная с Vitest 0.31.0, вы можете расширить интерфейс
Assertion
по умолчанию в файле декларации окружения (например:vitest.d.ts
) с помощью следующего кода:tsinterface CustomMatchers<R = unknown> { toBeFoo(): R; } declare module 'vitest' { interface Assertion<T = any> extends CustomMatchers<T> {} interface AsymmetricMatchersContaining extends CustomMatchers {} }
WARNING
Не забудьте включить файл декларации окружения в ваш
tsconfig.json
.TIP
Для получения дополнительной информации ознакомьтесь с руководством по расширению матчеров.