Мок-функции
Вы можете создать мок-функцию для отслеживания ее выполнения с помощью метода vi.fn()
. Если вы хотите отслеживать метод уже существующего объекта, вы можете использовать метод vi.spyOn
:
import { vi } from 'vitest';
const fn = vi.fn();
fn('hello world');
fn.mock.calls[0] === ['hello world'];
const market = {
getApples: () => 100,
};
const getApplesSpy = vi.spyOn(market, 'getApples');
market.getApples();
getApplesSpy.mock.calls.length === 1;
Для проверки результата мока следует использовать утверждения для моков (например, toHaveBeenCalled
) в сочетании с expect
. Этот справочник API описывает доступные свойства и методы для управления поведением мок-функции.
TIP
Пользовательская реализация функции в приведенных ниже типах помечена обобщенным типом <T>
.
getMockImplementation
function getMockImplementation(): T | undefined;
Возвращает текущую реализацию мок-функции, если она существует.
Если мок был создан с помощью vi.fn
, он будет использовать предоставленный метод в качестве своей реализации.
Если мок был создан с помощью vi.spyOn
, он вернет undefined
, если не была предоставлена пользовательская реализация.
getMockName
function getMockName(): string;
Используйте этот метод для получения имени, присвоенного мок-функции с помощью метода .mockName(name)
. По умолчанию он вернет vi.fn()
.
mockClear
function mockClear(): MockInstance<T>;
Очищает всю информацию о вызовах мок-функции. После вызова все свойства в .mock
вернутся в исходное состояние. Этот метод не сбрасывает реализации мока. Он полезен для очистки моков между различными утверждениями тестов.
const person = {
greet: (name: string) => `Hello ${name}`,
};
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked');
expect(person.greet('Alice')).toBe('mocked');
expect(spy.mock.calls).toEqual([['Alice']]);
// очистить историю вызовов, но сохранить реализацию мока
spy.mockClear();
expect(spy.mock.calls).toEqual([]);
expect(person.greet('Bob')).toBe('mocked');
expect(spy.mock.calls).toEqual([['Bob']]);
Чтобы автоматически вызывать этот метод перед каждым тестом, включите параметр clearMocks
в конфигурации.
mockName
function mockName(name: string): MockInstance<T>;
Устанавливает внутреннее имя мок-функции. Это полезно для идентификации мок-функции при сбое проверки.
mockImplementation
function mockImplementation(fn: T): MockInstance<T>;
Принимает функцию, которая будет использоваться в качестве реализации мока. TypeScript ожидает, что аргументы и тип возвращаемого значения будут соответствовать таковым у оригинальной функции.
const mockFn = vi.fn().mockImplementation((apples: number) => apples + 1);
// или: vi.fn(apples => apples + 1);
const NelliesBucket = mockFn(0);
const BobsBucket = mockFn(1);
NelliesBucket === 1; // true
BobsBucket === 2; // true
mockFn.mock.calls[0][0] === 0; // true
mockFn.mock.calls[1][0] === 1; // true
mockImplementationOnce
function mockImplementationOnce(fn: T): MockInstance<T>;
Принимает функцию, которая будет использоваться в качестве реализации мока. TypeScript ожидает, что аргументы и тип возвращаемого значения будут соответствовать таковым у исходной функции. Этот метод может быть объединен в цепочку для получения различных результатов для нескольких вызовов функции.
const myMockFn = vi
.fn()
.mockImplementationOnce(() => true) // 1-й вызов
.mockImplementationOnce(() => false); // 2-й вызов
myMockFn(); // 1-й вызов: true
myMockFn(); // 2-й вызов: false
Когда у мок-функции заканчиваются одноразовые реализации, она вызовет реализацию по умолчанию, установленную с помощью vi.fn(() => defaultValue)
или .mockImplementation(() => defaultValue)
, если таковые были заданы:
const myMockFn = vi
.fn(() => 'default')
.mockImplementationOnce(() => 'first call')
.mockImplementationOnce(() => 'second call');
// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
withImplementation
function withImplementation(fn: T, cb: () => void): MockInstance<T>;
function withImplementation(
fn: T,
cb: () => Promise<void>
): Promise<MockInstance<T>>;
Временно переопределяет исходную реализацию мок-функции на время выполнения колбэка.
const myMockFn = vi.fn(() => 'original');
myMockFn.withImplementation(
() => 'temp',
() => {
myMockFn(); // 'temp'
}
);
myMockFn(); // 'original'
Может использоваться с асинхронным колбэком. Метод должен быть дождан (или выполнен с await
), чтобы исходная реализация была восстановлена после этого.
test('async callback', async () => { // Добавлено async
const myMockFn = vi.fn(() => 'original');
// Мы дожидаемся выполнения этого вызова, так как колбэк асинхронный
await myMockFn.withImplementation(
() => 'temp',
async () => {
myMockFn(); // 'temp'
}
);
myMockFn(); // 'original'
});
Обратите внимание, что этот метод имеет приоритет над mockImplementationOnce
.
mockRejectedValue
function mockRejectedValue(value: unknown): MockInstance<T>;
Принимает ошибку, которая будет отклонена при вызове асинхронной функции.
const asyncMock = vi.fn().mockRejectedValue(new Error('Async error'));
await asyncMock(); // выбрасывает Error('Async error')
mockRejectedValueOnce
function mockRejectedValueOnce(value: unknown): MockInstance<T>;
Принимает значение, которое будет отклонено при следующем вызове функции. Если вызвано последовательно, каждый последующий вызов будет отклонять указанное значение.
const asyncMock = vi
.fn()
.mockResolvedValueOnce('first call')
.mockRejectedValueOnce(new Error('Async error'));
await asyncMock(); // 'first call'
await asyncMock(); // выбрасывает Error('Async error')
mockReset
function mockReset(): MockInstance<T>;
Выполняет те же действия, что и mockClear
, и восстанавливает внутреннюю реализацию до исходной функции. Это также обнуляет все одноразовые реализации.
Обратите внимание, что сброс мок-функции, созданной с помощью vi.fn()
, установит ее реализацию как пустую функцию, которая возвращает undefined
. Сброс мок-функции, созданной с помощью vi.fn(impl)
, восстановит ее реализацию до impl
.
Это полезно, когда вы хотите сбросить мок-функцию до ее исходного состояния.
const person = {
greet: (name: string) => `Hello ${name}`,
};
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked');
expect(person.greet('Alice')).toBe('mocked');
expect(spy.mock.calls).toEqual([['Alice']]);
// очистить историю вызовов и сбросить реализацию, но метод все еще отслеживается
spy.mockReset();
expect(spy.mock.calls).toEqual([]);
expect(person.greet).toBe(spy);
expect(person.greet('Bob')).toBe('Hello Bob');
expect(spy.mock.calls).toEqual([['Bob']]);
Чтобы автоматически вызывать этот метод перед каждым тестом, включите настройку mockReset
в конфигурации.
mockRestore
function mockRestore(): MockInstance<T>;
Выполняет те же действия, что и mockReset
, и восстанавливает исходные дескрипторы объектов, за которыми ведется наблюдение.
Обратите внимание, что восстановление мок-функции, созданной с помощью vi.fn()
, установит ее реализацию как пустую функцию, которая возвращает undefined
. Восстановление мок-функции, созданной с помощью vi.fn(impl)
, восстановит ее реализацию до impl
.
const person = {
greet: (name: string) => `Hello ${name}`,
};
const spy = vi.spyOn(person, 'greet').mockImplementation(() => 'mocked');
expect(person.greet('Alice')).toBe('mocked');
expect(spy.mock.calls).toEqual([['Alice']]);
// очистить историю вызовов и восстановить метод отслеживаемого объекта
spy.mockRestore();
expect(spy.mock.calls).toEqual([]);
expect(person.greet).not.toBe(spy);
expect(person.greet('Bob')).toBe('Hello Bob');
expect(spy.mock.calls).toEqual([]);
Чтобы автоматически вызывать этот метод перед каждым тестом, включите настройку restoreMocks
в конфигурации.
mockResolvedValue
function mockResolvedValue(value: Awaited<ReturnType<T>>): MockInstance<T>;
Принимает значение, которое будет разрешено при вызове асинхронной функции. TypeScript будет принимать только значения, тип которых соответствует типу возвращаемого значения исходной функции.
const asyncMock = vi.fn().mockResolvedValue(42);
await asyncMock(); // 42
mockResolvedValueOnce
function mockResolvedValueOnce(value: Awaited<ReturnType<T>>): MockInstance<T>;
Принимает значение, которое будет разрешено при следующем вызове функции. TypeScript будет принимать только значения, тип которых соответствует типу возвращаемого значения исходной функции. Если вызвано последовательно, каждый последующий вызов будет разрешать указанное значение.
const asyncMock = vi
.fn()
.mockResolvedValue('default')
.mockResolvedValueOnce('first call')
.mockResolvedValueOnce('second call');
await asyncMock(); // first call
await asyncMock(); // second call
await asyncMock(); // default
await asyncMock(); // default
mockReturnThis
function mockReturnThis(): MockInstance<T>;
Используйте это, если вам нужно вернуть контекст this
из метода, без выполнения его фактической реализации. Это сокращение для:
spy.mockImplementation(function () {
return this;
});
mockReturnValue
function mockReturnValue(value: ReturnType<T>): MockInstance<T>;
Принимает значение, которое будет возвращено при каждом вызове мок-функции. TypeScript будет принимать только значения, тип которых соответствует типу возвращаемого значения исходной функции.
const mock = vi.fn();
mock.mockReturnValue(42);
mock(); // 42
mock.mockReturnValue(43);
mock(); // 43
mockReturnValueOnce
function mockReturnValueOnce(value: ReturnType<T>): MockInstance<T>;
Принимает значение, которое будет возвращено при следующем вызове мок-функции. TypeScript будет принимать только значения, тип которых соответствует типу возвращаемого значения исходной функции.
Когда у мок-функции заканчиваются одноразовые реализации, она вызовет реализацию по умолчанию, установленную с помощью vi.fn(() => defaultValue)
или .mockImplementation(() => defaultValue)
, если они были вызваны:
const myMockFn = vi
.fn()
.mockReturnValue('default')
.mockReturnValueOnce('first call')
.mockReturnValueOnce('second call');
// 'first call', 'second call', 'default', 'default'
console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
mock.calls
const calls: Parameters<T>[];
Это массив, содержащий все аргументы для каждого вызова мок-функции. Каждый элемент массива представляет собой набор аргументов соответствующего вызова.
const fn = vi.fn();
fn('arg1', 'arg2');
fn('arg3');
fn.mock.calls ===
[
['arg1', 'arg2'], // первый вызов
['arg3'], // второй вызов
];
mock.lastCall
const lastCall: Parameters<T> | undefined;
Это свойство содержит аргументы последнего вызова мок-функции. Если мок-функция не была вызвана, оно вернет undefined
.
mock.results
interface MockResultReturn<T> {
type: 'return';
/**
* Значение, которое было возвращено функцией.
* Если функция вернула Promise, то это будет разрешенное значение.
*/
value: T;
}
interface MockResultIncomplete {
type: 'incomplete';
value: undefined;
}
interface MockResultThrow {
type: 'throw';
/**
* Ошибка, которая была выброшена во время выполнения функции.
*/
value: any;
}
type MockResult<T> =
| MockResultReturn<T>
| MockResultThrow
| MockResultIncomplete;
const results: MockResult<ReturnType<T>>[];
Это массив, содержащий результаты всех вызовов функции. Каждый элемент массива — это объект со свойствами type
и value
. Доступные типы:
'return'
- функция завершилась без ошибок.'throw'
- функция выбросила исключение.
Свойство value
содержит возвращенное значение или выброшенную ошибку. Если функция вернула Promise
, то result
всегда будет 'return'
, даже если промис был отклонен.
const fn = vi
.fn()
.mockReturnValueOnce('result')
.mockImplementationOnce(() => {
throw new Error('thrown error');
});
const result = fn(); // вернул 'result'
try {
fn(); // выбросил Error
} catch {}
fn.mock.results ===
[
// первый результат
{
type: 'return',
value: 'result',
},
// последний результат
{
type: 'throw',
value: Error,
},
];
mock.settledResults
interface MockSettledResultFulfilled<T> {
type: 'fulfilled';
value: T;
}
interface MockSettledResultRejected {
type: 'rejected';
value: any;
}
export type MockSettledResult<T> =
| MockSettledResultFulfilled<T>
| MockSettledResultRejected;
const settledResults: MockSettledResult<Awaited<ReturnType<T>>>[];
Массив, содержащий все значения, которые были выполнены (resolved) или отклонены (rejected) функцией.
Этот массив будет пустым, если функция еще не была разрешена или отклонена.
const fn = vi.fn().mockResolvedValueOnce('result');
const result = fn();
fn.mock.settledResults === [];
await result;
fn.mock.settledResults ===
[
{
type: 'fulfilled',
value: 'result',
},
];
mock.invocationCallOrder
const invocationCallOrder: number[];
Это свойство возвращает порядок выполнения мок-функции. Это массив чисел, которые являются общими для всех созданных моков.
const fn1 = vi.fn();
const fn2 = vi.fn();
fn1();
fn2();
fn1();
fn1.mock.invocationCallOrder === [1, 3];
fn2.mock.invocationCallOrder === [2];
mock.contexts
const contexts: ThisParameterType<T>[];
Это свойство представляет собой массив значений this
, используемых при каждом вызове мок-функции.
const fn = vi.fn();
const context = {};
fn.apply(context);
fn.call(context);
fn.mock.contexts[0] === context;
fn.mock.contexts[1] === context;
mock.instances
const instances: ReturnType<T>[];
Это свойство представляет собой массив, содержащий все экземпляры, которые были созданы при вызове мок-функции с ключевым словом new
. Обратите внимание, что это реальный контекст (this
) функции, а не возвращаемое значение.
WARNING
Если экземпляр мок-функции был создан с помощью new MyClass()
, то mock.instances
будет массивом с одним значением:
const MyClass = vi.fn();
const a = new MyClass();
MyClass.mock.instances[0] === a;
Если вы возвращаете значение из конструктора, оно не будет находиться в массиве instances
, а вместо этого будет внутри results
:
const Spy = vi.fn(() => ({ method: vi.fn() }));
const a = new Spy();
Spy.mock.instances[0] !== a;
Spy.mock.results[0] === a;