expect
Следующие типы используются в приведенных ниже сигнатурах типов:
type Awaitable<T> = T | PromiseLike<T>;expect используется для создания утверждений. В этом контексте утверждения — это функции, которые можно вызывать для проверки истинности утверждения. Vitest по умолчанию предоставляет утверждения chai, а также совместимые с Jest утверждения, построенные на основе chai. В отличие от Jest, Vitest поддерживает сообщение об ошибке в качестве второго аргумента — если утверждение не выполняется, сообщение об ошибке будет содержать его.
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;
}Например, этот код утверждает, что значение 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.
Также 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(4); // привел к сбою и прерыванию теста, все предыдущие ошибки будут выведены
expect.soft(1 + 3).toBe(5); // не запускать
});WARNING
expect.soft может использоваться только внутри функции test.
poll
interface ExpectPoll extends ExpectStatic {
(actual: () => T, options: { interval; timeout; message }): Promise<
Assertions<T>
>;
}expect.poll повторно выполняет утверждение, пока оно не будет выполнено успешно. Вы можете настроить, сколько раз Vitest должен повторно запускать колбэк expect.poll, установив параметры interval и timeout.
Если внутри колбэка expect.poll выбрасывается ошибка, Vitest будет повторять попытки, пока не истечет время ожидания.
import { expect, test } from 'vitest';
test('element exists', async () => {
asyncInjectElement();
await expect.poll(() => document.querySelector('.element')).toBeTruthy();
});WARNING
expect.poll делает каждое утверждение асинхронным, поэтому вам нужно его ожидать. Начиная с Vitest 3, если вы забудете его ожидать, тест завершится с соответствующим предупреждением.
expect.poll не работает с несколькими сопоставителями:
- Сопоставители снимков не поддерживаются, так как они всегда будут проходить успешно. Если ваше условие нестабильно, рассмотрите возможность использования
vi.waitForдля его разрешения в первую очередь:
import { expect, vi } from 'vitest';
const flakyValue = await vi.waitFor(() => getFlakyValue());
expect(flakyValue).toMatchSnapshot();.resolvesи.rejectsне поддерживаются.expect.pollуже ожидает условие, если оно асинхронно.toThrowи его псевдонимы не поддерживаются, потому что условиеexpect.pollвсегда разрешается до того, как сопоставитель получит значение.
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 APItoBe
- Тип:
(value: any) => Awaitable<void>
toBe можно использовать для утверждения равенства примитивов или того, что объекты имеют одну и ту же ссылку. Это эквивалентно вызову expect(Object.is(3, 3)).toBe(true). Если объекты не являются одним и тем же экземпляром, но вы хотите проверить идентичность их структур, вы можете использовать toEqual.
Например, приведенный ниже код проверяет, есть ли у торговца 13 яблок.
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; // та же ссылка
expect(stock).toBe(refStock);
});Старайтесь не использовать toBe с числами с плавающей запятой. Поскольку JavaScript округляет их, 0.1 + 0.2 не строго равно 0.3. Для надежного утверждения чисел с плавающей запятой используйте утверждение toBeCloseTo.
toBeCloseTo
- Тип:
(value: number, numDigits?: number) => Awaitable<void>
Используйте toBeCloseTo для сравнения чисел с плавающей запятой. Необязательный аргумент numDigits ограничивает количество цифр для проверки после десятичной точки. Например:
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" удалено
expect(0.2 + 0.1).toBeCloseTo(0.3, 5);
// ничего из 0.30000000000000004 не удалено
expect(0.2 + 0.1).not.toBeCloseTo(0.3, 50);
});toBeDefined
- Тип:
() => Awaitable<void>
toBeDefined утверждает, что значение не равно undefined. Полезный вариант использования — проверить, вернула ли функция какое-либо значение.
import { expect, test } from 'vitest';
function getApples() {
return 3;
}
test('function returned something', () => {
expect(getApples()).toBeDefined();
});toBeUndefined
- Тип:
() => Awaitable<void>
В отличие от toBeDefined, toBeUndefined утверждает, что значение равно undefined. Полезный вариант использования — проверить, что функция ничего не вернула.
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
- Тип:
() => Awaitable<void>
toBeTruthy утверждает, что значение истинно при преобразовании в логический тип. Полезно, если вам не важно конкретное значение, а лишь то, что оно может быть преобразовано в true.
Например, в данном коде вам не важно возвращаемое значение stocks.getInfo — это может быть сложный объект, строка или что-то иное. Код будет функционировать корректно.
import { Stocks } from './stocks.js';
const stocks = new Stocks();
stocks.sync('Bill');
if (stocks.getInfo('Bill')) {
stocks.sell('apples', 'Bill');
}Итак, если вы хотите проверить, что stocks.getInfo является истинным, вы можете написать:
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();
});Все в JavaScript считается истинным, кроме false, null, undefined, NaN, 0, -0, 0n, "" и document.all.
toBeFalsy
- Тип:
() => Awaitable<void>
toBeFalsy утверждает, что значение ложно при преобразовании в логический тип. Полезно, если вам не важно само значение, а просто нужно знать, может ли оно быть преобразовано в false.
Например, имея этот код, вам не важно возвращаемое значение stocks.stockFailed — оно может вернуть любое ложное значение, но код все равно будет работать.
import { Stocks } from './stocks.js';
const stocks = new Stocks();
stocks.sync('Bill');
if (!stocks.stockFailed('Bill')) {
stocks.sell('apples', 'Bill');
}Итак, если вы хотите проверить, что stocks.stockFailed будет ложным, вы можете написать:
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();
});Все в JavaScript считается истинным, кроме false, null, undefined, NaN, 0, -0, 0n, "" и document.all.
toBeNull
- Тип:
() => Awaitable<void>
toBeNull просто утверждает, что что-то равно null. Псевдоним для .toBe(null).
import { expect, test } from 'vitest';
function apples() {
return null;
}
test("we don't have apples", () => {
expect(apples()).toBeNull();
});toBeNaN
- Тип:
() => Awaitable<void>
toBeNaN просто утверждает, что что-то равно NaN. Псевдоним для .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
- Тип:
(sample: Array<any>) => any
toBeOneOf утверждает, что значение соответствует любому из значений в предоставленном массиве.
import { expect, test } from 'vitest';
test('fruit is one of the allowed values', () => {
expect(fruit).toBeOneOf(['apple', 'banana', 'orange']);
});Асимметричный сопоставитель особенно полезен при тестировании необязательных свойств, которые могут быть либо null, либо 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
Вы можете использовать expect.not с этим сопоставителем, чтобы убедиться, что значение НЕ соответствует ни одному из предоставленных вариантов.
toBeTypeOf
- Тип:
(c: 'bigint' | 'boolean' | 'function' | 'number' | 'object' | 'string' | 'symbol' | 'undefined') => Awaitable<void>
toBeTypeOf утверждает, что фактическое значение имеет тип полученного типа.
import { expect, test } from 'vitest';
const actual = 'stock';
test('stock is type of string', () => {
expect(actual).toBeTypeOf('string');
});toBeInstanceOf
- Тип:
(c: any) => Awaitable<void>
toBeInstanceOf утверждает, что фактическое значение является экземпляром полученного класса.
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
- Тип:
(n: number | bigint) => Awaitable<void>
toBeGreaterThan утверждает, что фактическое значение больше полученного. Равные значения приведут к ошибке теста.
import { 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 утверждает, что фактическое значение больше или равно полученному.
import { 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 утверждает, что фактическое значение меньше полученного. Равные значения приведут к ошибке теста.
import { 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 утверждает, что фактическое значение меньше или равно полученному.
import { 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 в этом примере:
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
Для объектов Error также сравниваются неперечисляемые свойства, такие как name, message, cause и AggregateError.errors. Для Error.cause сравнение выполняется асимметрично:
// успех
expect(new Error('hi', { cause: 'x' })).toEqual(new Error('hi'));
// неудача
expect(new Error('hi')).toEqual(new Error('hi', { cause: 'x' }));Чтобы проверить, было ли что-то выброшено, используйте утверждение toThrowError.
toStrictEqual
- Тип:
(received: any) => Awaitable<void>
toStrictEqual утверждает, что фактическое значение равно полученному или имеет ту же структуру, если это объект (сравнивает их рекурсивно), и того же типа.
Отличия от .toEqual:
- Проверяются ключи со свойствами
undefined. Например,{a: undefined, b: 2}не соответствует{b: 2}при использовании.toStrictEqual. - Проверяется разреженность массива. Например,
[, 1]не соответствует[undefined, 1]при использовании.toStrictEqual. - Типы объектов проверяются на равенство. Например, экземпляр класса с полями
aиbне будет равен литеральному объекту с полямиaиb.
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
- Тип:
(received: string) => Awaitable<void>
toContain утверждает, что фактическое значение находится в массиве. toContain также может проверять, является ли строка подстрокой другой строки. Если вы запускаете тесты в браузере, это утверждение также может проверять, содержится ли класс в classList или один элемент внутри другого.
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');
// элемент содержит класс
expect(element.classList).toContain('flex');
// элемент является дочерним по отношению к другому
expect(document.querySelector('#wrapper')).toContain(element);
});toContainEqual
- Тип:
(received: any) => Awaitable<void>
toContainEqual утверждает, что элемент с определенной структурой и значениями содержится в массиве. Он работает аналогично toEqual для каждого элемента.
import { 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 и оно установлено на определенное числовое значение.
import { expect, test } from 'vitest';
test('toHaveLength', () => {
expect('abc').toHaveLength(3);
expect([1, 2, 3]).toHaveLength(3);
expect('').not.toHaveLength(3); // длина не равна 3
expect({ length: 3 }).toHaveLength(3);
});toHaveProperty
- Тип:
(key: any, received?: any) => Awaitable<void>
toHaveProperty утверждает, что свойство по предоставленному ключу key существует для объекта.
Вы также можете предоставить необязательный аргумент значения, который используется для глубокого сравнения полученного значения свойства, аналогично сопоставителю toEqual.
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'); // проверяет существование ключа
expect(invoice).toHaveProperty('total_amount', 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'); // точечная нотация также работает
// Глубокая ссылка с использованием массива, содержащего keyPath
expect(invoice).toHaveProperty(['items', 0, 'type'], 'apples');
expect(invoice).toHaveProperty(['items', '0', 'type'], 'apples'); // строковая нотация также работает
// Оберните ключ в массив, чтобы предотвратить его интерпретацию как глубокой ссылки
expect(invoice).toHaveProperty(['P.O'], '12345');
});toMatch
- Тип:
(received: string | regexp) => Awaitable<void>
toMatch утверждает, что строка соответствует регулярному выражению или строке.
import { expect, test } from 'vitest';
test('top fruits', () => {
expect('top fruits include apple, orange and grape').toMatch(/apple/);
expect('applefruits').toMatch('fruit'); // toMatch также может принимать строку
});toMatchObject
- Тип:
(received: object | array) => Awaitable<void>
toMatchObject утверждает, что объект соответствует подмножеству свойств объекта.
Вы также можете передать массив объектов. Это полезно, если вы хотите проверить точное совпадение двух массивов по количеству элементов, в отличие от arrayContaining, который допускает наличие дополнительных элементов в проверяемом массиве.
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', () => {
// Проверка соответствия массива объектов
expect([{ foo: 'bar' }, { baz: 1 }]).toMatchObject([
{ foo: 'bar' },
{ baz: 1 },
]);
});toThrowError
Тип:
(received: any) => Awaitable<void>Псевдоним:
toThrow
toThrowError утверждает, что функция выбрасывает ошибку при вызове.
Вы можете предоставить необязательный аргумент для проверки того, что выбрасывается конкретная ошибка:
RegExp: сообщение об ошибке соответствует шаблонуstring: сообщение об ошибке содержит подстрокуError,AsymmetricMatcher: сравнение с полученным объектом аналогичноtoEqual(received)
TIP
Вы должны обернуть код в функцию, иначе ошибка не будет перехвачена, и тест провалится.
Это не относится к асинхронным вызовам, так как rejects корректно обрабатывает промисы:
test('expect rejects toThrow', async ({ expect }) => {
const promise = Promise.reject(new Error('Test'));
await expect(promise).rejects.toThrowError();
});Например, если мы хотим проверить, что getFruitStock('pineapples') выбрасывает ошибку, мы можем написать:
import { expect, test } from 'vitest';
function getFruitStock(type: string) {
if (type === 'pineapples') {
throw new Error('Pineapples are not in stock');
}
// Выполнить другие действия
}
test('throws on pineapples', () => {
// Проверить, что сообщение об ошибке содержит подстроку "stock": следующие утверждения эквивалентны
expect(() => getFruitStock('pineapples')).toThrowError(/stock/);
expect(() => getFruitStock('pineapples')).toThrowError('stock');
// Проверить точное сообщение об ошибке
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
Для тестирования асинхронных функций используйте в сочетании с rejects.
function getAsyncFruitStock() {
return Promise.reject(new Error('empty'));
}
test('throws on pineapples', async () => {
await expect(() => getAsyncFruitStock()).rejects.toThrowError('empty');
});toMatchSnapshot
- Тип:
<T>(shape?: Partial<T> | string, hint?: string) => void
Это гарантирует, что значение соответствует актуальному снимку.
Вы можете предоставить необязательный строковый аргумент hint, который добавляется к имени теста. Хотя Vitest всегда добавляет число в конец имени снимка, короткие описательные подсказки могут быть более полезными, чем числа, для однозначной идентификации нескольких снимков в одном блоке it или test. Vitest сортирует снимки по имени в соответствующем файле .snap.
TIP
Когда снимок не совпадает и приводит к сбою теста, если несоответствие ожидаемо, вы можете нажать клавишу u для однократного обновления снимка. Или вы можете передать параметры CLI -u или --update, чтобы Vitest всегда обновлял тесты.
import { expect, test } from 'vitest';
test('matches snapshot', () => {
const data = { foo: new Set(['bar', 'snapshot']) };
expect(data).toMatchSnapshot();
});Вы также можете предоставить шаблон объекта, если вы тестируете только его структуру и не требуете 100% соответствия:
import { expect, test } from 'vitest';
test('matches snapshot', () => {
const data = { foo: new Set(['bar', 'snapshot']) };
expect(data).toMatchSnapshot({ foo: expect.any(Set) });
});toMatchInlineSnapshot
- Тип:
<T>(shape?: Partial<T> | string, snapshot?: string, hint?: string) => void
Это гарантирует, что значение соответствует актуальному снимку.
Vitest добавляет и обновляет строковый аргумент inlineSnapshot непосредственно в файле теста (вместо внешнего файла .snap).
import { expect, test } from 'vitest';
test('matches inline snapshot', () => {
const data = { foo: new Set(['bar', 'snapshot']) };
// Vitest обновит следующее содержимое при обновлении снимка
expect(data).toMatchInlineSnapshot(`
{
"foo": Set {
"bar",
"snapshot",
},
}
`);
});Вы также можете предоставить шаблон объекта, если вы тестируете только его структуру и не требуете 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
- Тип:
<T>(filepath: string, hint?: string) => Promise<void>
Сравнить или обновить снимок с содержимым явно указанного файла (вместо файла .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');
});Обратите внимание, что поскольку операция с файловой системой является асинхронной, вам нужно использовать await с toMatchFileSnapshot(). Если await не используется, Vitest обрабатывает это как expect.soft, то есть код после оператора продолжит выполняться, даже если снимок не совпадает. После завершения теста Vitest проверит снимок и завершится неудачей, если есть несоответствие.
toThrowErrorMatchingSnapshot
- Тип:
(hint?: string) => void
То же, что и toMatchSnapshot, но ожидает то же значение, что и toThrowError.
toThrowErrorMatchingInlineSnapshot
- Тип:
(snapshot?: string, hint?: string) => void
То же, что и toMatchInlineSnapshot, но ожидает то же значение, что и toThrowError.
toHaveBeenCalled
- Тип:
() => 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');
expect(buySpy).not.toHaveBeenCalled();
market.buy('apples', 10);
expect(buySpy).toHaveBeenCalled();
});toHaveBeenCalledTimes
- Тип:
(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
- Тип:
(...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);
});toHaveBeenCalledBefore 3.0.0+
- Тип:
(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable<void>
Это утверждение проверяет, был ли один Mock вызван раньше другого 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+
- Тип:
(mock: MockInstance, failIfNoFirstInvocation?: boolean) => Awaitable<void>
Это утверждение проверяет, был ли один Mock вызван после другого 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+
- Тип:
(...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);
expect(buySpy).toHaveBeenCalledExactlyOnceWith('apples', 10);
});toHaveBeenLastCalledWith
- Тип:
(...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
- Тип:
(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
- Тип:
() => 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
- Тип:
(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>
Вы можете вызвать это утверждение, чтобы проверить, успешно ли функция вернула значение с заданными параметрами хотя бы один раз. Для использования этого утверждения необходимо передать функцию-шпион в 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>
Вы можете вызвать это утверждение, чтобы проверить, успешно ли функция вернула заданное значение при последнем вызове. Для использования этого утверждения необходимо передать функцию-шпион в 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>
Вы можете вызвать это утверждение, чтобы проверить, успешно ли функция вернула значение с заданными параметрами при конкретном вызове. Для использования этого утверждения необходимо передать функцию-шпион в 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
- Тип:
() => Awaitable<void>
Это утверждение проверяет, успешно ли функция разрешила значение хотя бы один раз (т.е. не была отклонена). Для использования этого утверждения необходимо передать функцию-шпион в expect.
Если функция вернула промис, но он еще не был разрешен, это вызовет ошибку.
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
- Тип:
(amount: number) => Awaitable<void>
Это утверждение проверяет, успешно ли функция разрешила значение ровно указанное количество раз (т.е. не была отклонена). Для использования этого утверждения необходимо передать функцию-шпион в expect.
Это будет считать только разрешенные промисы. Если функция вернула промис, но он еще не был разрешен, он не будет учтен.
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
- Тип:
(returnValue: any) => Awaitable<void>
Вы можете вызвать это утверждение, чтобы проверить, успешно ли функция разрешила заданное значение хотя бы один раз. Для использования этого утверждения необходимо передать функцию-шпион в expect.
Если функция вернула промис, но он еще не был разрешен, это вызовет ошибку.
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
- Тип:
(returnValue: any) => Awaitable<void>
Вы можете вызвать это утверждение, чтобы проверить, успешно ли функция разрешила заданное значение при последнем вызове. Для использования этого утверждения необходимо передать функцию-шпион в expect.
Если функция вернула промис, но он еще не был разрешен, это вызовет ошибку.
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
- Тип:
(time: number, returnValue: any) => Awaitable<void>
Вы можете вызвать это утверждение, чтобы проверить, успешно ли функция разрешила заданное значение при конкретном вызове. Для использования этого утверждения необходимо передать функцию-шпион в expect.
Если функция вернула промис, но он еще не был разрешен, это вызовет ошибку.
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
- Тип:
(predicate: (value: any) => boolean) => Awaitable<void>
Это утверждение проверяет, удовлетворяет ли значение определенному предикату.
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
- Тип:
Promisify<Assertions>
resolves предназначен для удаления шаблонного кода при утверждении асинхронного кода. Используйте его для извлечения значения из ожидающего промиса и утверждения его значения с помощью обычных утверждений. Если промис отклоняется, утверждение завершится неудачей.
Он возвращает тот же объект Assertions, но все сопоставители теперь возвращают Promise, поэтому вам нужно будет его await. Также работает с утверждениями chai.
Например, если у вас есть функция, которая делает вызов API и возвращает некоторые данные, вы можете использовать этот код для утверждения ее возвращаемого значения:
import { 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).
Начиная с Vitest 3, если метод не ожидается, Vitest покажет предупреждение в конце теста. В Vitest 4 тест будет помечен как "неудачный", если утверждение не ожидается.
rejects
- Тип:
Promisify<Assertions>
rejects предназначен для удаления шаблонного кода при утверждении асинхронного кода. Используйте его для извлечения причины отклонения промиса и утверждения его значения с помощью обычных утверждений. Если промис успешно разрешается, утверждение завершится неудачей.
Он возвращает тот же объект Assertions, но все сопоставители теперь возвращают Promise, поэтому вам нужно будет его await. Также работает с утверждениями chai.
Например, если у вас есть функция, которая завершается сбоем при вызове, вы можете использовать этот код для утверждения причины:
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 теперь возвращает промис, поэтому вы ОБЯЗАНЫ его ожидать
await expect(buyApples()).rejects.toThrow('no id');
});WARNING
Если утверждение не ожидается, то у вас будет ложноположительный тест, который будет проходить каждый раз. Чтобы убедиться, что утверждения действительно вызывались, вы можете использовать expect.assertions(number).
Начиная с Vitest 3, если метод не ожидается, Vitest покажет предупреждение в конце теста. В Vitest 4 тест будет помечен как "неудачный", если утверждение не ожидается.
expect.assertions
- Тип:
(count: number) => void
После завершения теста (успешного или неуспешного) проверьте, что во время теста было вызвано определенное количество утверждений. Это полезно для проверки вызова асинхронного кода.
Например, если у нас есть функция, которая асинхронно вызывает два сопоставителя, мы можем утверждать, что они действительно были вызваны.
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
При использовании assertions с асинхронными параллельными тестами, expect из локального Test Context должен использоваться для корректного определения соответствующего теста.
expect.hasAssertions
- Тип:
() => void
После завершения теста (успешного или неуспешного) проверьте, что во время теста было вызвано хотя бы одно утверждение. Это полезно для проверки вызова асинхронного кода.
Например, если у вас есть код, который вызывает колбэк, мы можем сделать утверждение внутри этого колбэка. Однако тест всегда будет проходить, если мы не проверим, было ли это утверждение фактически вызвано.
import { expect, test } from 'vitest';
import { db } from './db.js';
const cbs = [];
function onSelect(cb) {
cbs.push(cb);
}
// после выбора из db, мы вызываем все колбэки
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, а также обрабатывать каждую ошибку отдельно, мы можем сделать это:
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:
// для обработки всех возможных ошибок
expect.unreachable('All error test must be handled');
break;
}
}
});expect.anything
- Тип:
() => any
Этот асимметричный сопоставитель, при использовании с проверкой равенства, всегда будет возвращать true. Полезно, если вы хотите убедиться в существовании свойства.
import { expect, test } from 'vitest';
test('object has "apples" key', () => {
expect({ apples: 22 }).toEqual({ apples: expect.anything() });
});expect.any
- Тип:
(constructor: unknown) => any
Этот асимметричный сопоставитель, при использовании с проверкой равенства, вернет true только в том случае, если значение является экземпляром указанного конструктора. Полезно, если у вас есть значение, которое генерируется каждый раз, и вы хотите лишь убедиться, что оно существует и имеет правильный тип.
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
- Тип:
(expected: any, precision?: number) => any
expect.closeTo полезен при сравнении чисел с плавающей запятой в свойствах объектов или элементах массивов. Если вам нужно сравнить число, используйте вместо этого .toBeCloseTo.
Необязательный аргумент precision ограничивает количество цифр для проверки после десятичной точки. Для значения по умолчанию 2 критерий проверки равенства равен Math.abs(expected - received) < 0.005 (то есть, 10 ** -2 / 2).
Например, этот тест проходит с точностью до 5 знаков:
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
- Тип:
<T>(expected: T[]) => any
При использовании в проверке равенства этот асимметричный сопоставитель вернет true, если значение является массивом и содержит указанные элементы.
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
Вы можете использовать expect.not с этим сопоставителем, чтобы отрицать ожидаемое значение.
expect.objectContaining
- Тип:
(expected: any) => any
При использовании в проверке равенства этот асимметричный сопоставитель вернет true, если значение имеет похожую структуру.
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
Вы можете использовать expect.not с этим сопоставителем, чтобы отрицать ожидаемое значение.
expect.stringContaining
- Тип:
(expected: any) => any
При использовании в проверке равенства этот асимметричный сопоставитель вернет true, если значение является строкой и содержит указанную подстроку.
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
Вы можете использовать expect.not с этим сопоставителем, чтобы отрицать ожидаемое значение.
expect.stringMatching
- Тип:
(expected: any) => any
При использовании в проверке равенства этот асимметричный сопоставитель вернет true, если значение является строкой и содержит указанную подстроку или соответствует регулярному выражению.
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
Вы можете использовать expect.not с этим сопоставителем, чтобы отрицать ожидаемое значение.
expect.addSnapshotSerializer
- Тип:
(plugin: PrettyFormatPlugin) => void
Этот метод добавляет пользовательские сериализаторы, используемые при создании снимка. Это расширенная возможность — если вы хотите узнать больше, пожалуйста, прочитайте руководство по пользовательским сериализаторам.
Если вы добавляете пользовательские сериализаторы, вы должны вызвать этот метод внутри setupFiles. Это затронет все снимки.
TIP
Если вы ранее использовали Vue CLI с Jest, вы можете установить jest-serializer-vue. В противном случае ваши снимки будут обернуты в строку, что приведет к экранированию символа " .
expect.extend
- Тип:
(matchers: MatchersObject) => void
Вы можете расширить стандартные сопоставители. Эта функция используется для расширения набора сопоставителей пользовательскими сопоставителями.
Таким образом, при определении сопоставителей вы также создаете асимметричные сопоставители, которые можно использовать, например, с 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
Если вы хотите, чтобы ваши сопоставители были доступны в каждом тесте, вы должны вызвать этот метод внутри setupFiles.
Эта функция совместима с expect.extend из Jest, поэтому любая библиотека, использующая ее для создания пользовательских сопоставителей, будет работать с Vitest.
Если вы используете TypeScript, начиная с Vitest 0.31.0 вы можете расширить стандартный интерфейс Assertion в файле объявлений типов (например, vitest.d.ts) с помощью следующего кода:
interface CustomMatchers<R = unknown> {
toBeFoo: () => R;
}
declare module 'vitest' {
interface Assertion<T = any> extends CustomMatchers<T> {}
interface AsymmetricMatchersContaining extends CustomMatchers {}
}WARNING
Не забудьте включить файл объявлений типов в ваш tsconfig.json.
TIP
Если вы хотите узнать больше, ознакомьтесь с руководством по расширению сопоставителей.
expect.addEqualityTesters
- Тип:
(tester: Array<Tester>) => void
Вы можете использовать этот метод для определения пользовательских тестеров — методов, используемых сопоставителями для проверки равенства двух объектов. Он совместим с expect.addEqualityTesters из 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')
);
});