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 API
toBe
- Тип:
(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')
);
});