Vitest
Vitest предоставляет вспомогательные функции, которые помогут вам в работе, через свой вспомогательный объект vi
. Вы можете получить к нему доступ глобально (когда включена конфигурация globals), или импортировать его непосредственно из vitest
:
import { vi } from 'vitest';
Имитация модулей
В этом разделе описывается API, которое вы можете использовать при имитации модуля. Обратите внимание, что Vitest не поддерживает имитацию модулей, импортированных с помощью require()
.
vi.mock
- Тип:
(path: string, factory?: (importOriginal: () => unknown) => unknown) => void
Заменяет все импортированные модули по указанному path
другим модулем. Вы можете использовать настроенные алиасы Vite внутри пути. Вызов vi.mock
перемещается в начало файла, поэтому не имеет значения, где вы его вызываете. Он всегда будет выполнен перед всеми импортами. Если вам нужно обратиться к переменным из внешней области видимости, вы можете определить их внутри vi.hoisted
и сослаться на них внутри vi.mock
.
WARNING
vi.mock
работает только для модулей, которые были импортированы с помощью ключевого слова import
. Он не работает с require
.
Чтобы Vitest мог переместить vi.mock
в начало файла, он статически анализирует ваши файлы. Это означает, что vi
, импортированный не напрямую из пакета vitest
(например, из служебного файла), не может быть использован. Используйте vi.mock
с vi
, импортированным из vitest
, или включите параметр конфигурации globals
.
Vitest не будет имитировать модули, которые были импортированы внутри файла настройки, потому что они кэшируются к моменту запуска тестового файла. Вы можете вызвать vi.resetModules()
внутри vi.hoisted
, чтобы очистить кэш всех модулей перед запуском тестового файла.
WARNING
Режим браузера в настоящее время не поддерживает имитацию модулей. Вы можете отслеживать статус этой функции в GitHub issue.
Если factory
определен, все импорты вернут его результат. Vitest вызывает factory
только один раз и кэширует результаты для всех последующих импортов до тех пор, пока не будет вызван vi.unmock
или vi.doUnmock
.
В отличие от jest
, factory
может быть асинхронной функцией. Вы можете использовать vi.importActual
или хелпер с factory
, переданным в качестве первого аргумента, и получить исходный модуль внутри.
import { vi } from 'vitest';
// ---cut---
// при использовании JavaScript
vi.mock('./path/to/module.js', async importOriginal => {
const mod = await importOriginal();
return {
...mod,
// заменить некоторые экспорты
namedExport: vi.fn(),
};
});
// при использовании TypeScript
vi.mock('./path/to/module.js', async importOriginal => {
const mod = await importOriginal<typeof import('./path/to/module.js')>();
return {
...mod,
// заменить некоторые экспорты
namedExport: vi.fn(),
};
});
WARNING
vi.mock
перемещается в начало файла. Это означает, что независимо от того, где вы его написали (внутри beforeEach
или test
), он будет вызван раньше.
Это также означает, что вы не можете использовать какие-либо переменные внутри factory
, которые определены вне factory
.
Если вам нужно использовать переменные внутри factory
, попробуйте vi.doMock
. Он работает так же, но не перемещается. Следует помнить, что он имитирует только те импорты, которые происходят после вызова vi.doMock
.
Вы также можете ссылаться на переменные, определенные методом vi.hoisted
, если он был объявлен до vi.mock
:
import { namedExport } from './path/to/module.js';
const mocks = vi.hoisted(() => {
return {
namedExport: vi.fn(),
};
});
vi.mock('./path/to/module.js', () => {
return {
namedExport: mocks.namedExport,
};
});
vi.mocked(namedExport).mockReturnValue(100);
expect(namedExport()).toBe(100);
expect(namedExport).toBe(mocks.namedExport);
WARNING
Если вы имитируете модуль с экспортом по умолчанию, вам нужно будет предоставить ключ default
внутри возвращаемого объекта factory function. Это особенность ES-модулей; поэтому документация jest
может отличаться, поскольку jest
использует модули CommonJS. Например,
vi.mock('./path/to/module.js', () => {
return {
default: { myDefaultKey: vi.fn() },
namedExport: vi.fn(),
// etc...
};
});
Если рядом с файлом, который вы имитируете, есть папка __mocks__
, и factory
не предоставлен, Vitest попытается найти файл с тем же именем в подпапке __mocks__
и использовать его в качестве фактического модуля. Если вы имитируете зависимость, Vitest попытается найти папку __mocks__
в корне проекта (по умолчанию process.cwd()
). Вы можете указать Vitest, где находятся зависимости, с помощью параметра конфигурации deps.moduleDirectories.
Например, у вас есть следующая структура файлов:
- __mocks__
- axios.js
- src
__mocks__
- increment.js
- increment.js
- tests
- increment.test.js
Если вы вызываете vi.mock
в тестовом файле без предоставленного factory
, он найдет файл в папке __mocks__
для использования в качестве модуля:
// increment.test.js
import { vi } from 'vitest';
// axios - это экспорт по умолчанию из `__mocks__/axios.js`
import axios from 'axios';
// increment - это именованный экспорт из `src/__mocks__/increment.js`
import { increment } from '../increment.js';
vi.mock('axios');
vi.mock('../increment.js');
axios.get(`/apples/${increment(1)}`);
WARNING
Обратите внимание, что если вы не вызываете vi.mock
, модули не будут имитироваться автоматически. Чтобы воспроизвести поведение автоматического имитирования Jest, вы можете вызвать vi.mock
для каждого необходимого модуля внутри setupFiles
.
Если нет папки __mocks__
или предоставленного factory
, Vitest импортирует исходный модуль и автоматически имитирует все его экспорты. Правила, которые применяются, см. в алгоритме.
vi.doMock
- Тип:
(path: string, factory?: (importOriginal: () => unknown) => unknown) => void
То же самое, что и vi.mock
, но он не перемещается в начало файла, поэтому вы можете ссылаться на переменные в глобальной области видимости файла. Будет имитирован следующий динамический импорт этого модуля.
WARNING
Это не будет имитировать модули, которые были импортированы до этого вызова. Не забывайте, что все статические импорты в ESM всегда поднимаются, поэтому размещение этого перед статическим импортом не заставит его быть вызванным перед импортом:
vi.doMock('./increment.js'); // это будет вызвано _после_ оператора импорта
import { increment } from './increment.js';
// ./increment.js
export function increment(number) {
return number + 1;
}
import { beforeEach, test } from 'vitest';
import { increment } from './increment.js';
// модуль не имитируется, потому что vi.doMock еще не вызван
increment(1) === 2;
let mockedIncrement = 100;
beforeEach(() => {
// вы можете получить доступ к переменным внутри factory
vi.doMock('./increment.js', () => ({ increment: () => ++mockedIncrement }));
});
test('importing the next module imports mocked one', async () => {
// исходный импорт НЕ БЫЛ ИМИТИРОВАН, потому что vi.doMock вычисляется ПОСЛЕ импортов
expect(increment(1)).toBe(2);
const { increment: mockedIncrement } = await import('./increment.js');
// новый динамический импорт возвращает имитированный модуль
expect(mockedIncrement(1)).toBe(101);
expect(mockedIncrement(1)).toBe(102);
expect(mockedIncrement(1)).toBe(103);
});
vi.mocked
- Тип:
<T>(obj: T, deep?: boolean) => MaybeMockedDeep<T>
- Тип:
<T>(obj: T, options?: { partial?: boolean; deep?: boolean }) => MaybePartiallyMockedDeep<T>
Типовой помощник для TypeScript. Просто возвращает переданный объект.
Когда partial
имеет значение true
, он будет ожидать Partial<T>
в качестве возвращаемого значения. По умолчанию это только заставит TypeScript поверить, что значения первого уровня имитированы. Вы можете передать { deep: true }
в качестве второго аргумента, чтобы сообщить TypeScript, что весь объект имитирован, если это действительно так.
import example from './example.js';
vi.mock('./example.js');
test('1 + 1 equals 10', async () => {
vi.mocked(example.calc).mockReturnValue(10);
expect(example.calc(1, '+', 1)).toBe(10);
});
vi.importActual
- Тип:
<T>(path: string) => Promise<T>
Импортирует модуль, обходя все проверки, должен ли он быть имитирован. Может быть полезно, если вы хотите частично имитировать модуль.
vi.mock('./example.js', async () => {
const axios = await vi.importActual('./example.js');
return { ...axios, get: vi.fn() };
});
vi.importMock
- Тип:
<T>(path: string) => Promise<MaybeMockedDeep<T>>
Импортирует модуль со всеми его свойствами (включая вложенные свойства), имитированными. Следует тем же правилам, что и vi.mock
. Правила, которые применяются, см. в алгоритме.
vi.unmock
- Тип:
(path: string) => void
Удаляет модуль из реестра моков. Все вызовы import вернут исходный модуль, даже если он был имитирован ранее. Этот вызов перемещается в начало файла, поэтому он будет отменять имитацию только модулей, которые были определены в setupFiles
, например.
vi.doUnmock
- Тип:
(path: string) => void
То же самое, что и vi.unmock
, но не перемещается в начало файла. Следующий импорт модуля импортирует исходный модуль вместо имитированного. Это не отменит имитацию модулей, которые были импортированы ранее.
// ./increment.js
export function increment(number) {
return number + 1;
}
import { increment } from './increment.js';
// increment уже имитирован, потому что vi.mock поднят
increment(1) === 100;
// это поднято, и factory вызывается перед импортом в строке 1
vi.mock('./increment.js', () => ({ increment: () => 100 }));
// все вызовы имитированы, и `increment` всегда возвращает 100
increment(1) === 100;
increment(30) === 100;
// это не поднято, поэтому другой импорт вернет неимитированный модуль
vi.doUnmock('./increment.js');
// это ВСЕ ЕЩЕ возвращает 100, потому что `vi.doUnmock` не переоценивает модуль
increment(1) === 100;
increment(30) === 100;
// следующий импорт не имитирован, теперь `increment` - это исходная функция, которая возвращает count + 1
const { increment: unmockedIncrement } = await import('./increment.js');
unmockedIncrement(1) === 2;
unmockedIncrement(30) === 31;
vi.resetModules
- Тип:
() => Vitest
Сбрасывает реестр модулей, очищая кэш всех модулей. Это позволяет переоценивать модули при повторном импорте. Импорты верхнего уровня не могут быть переоценены. Может быть полезно для изоляции модулей, где локальное состояние конфликтует между тестами.
import { vi } from 'vitest';
import { data } from './data.js'; // Не будет переоценен перед каждым тестом
beforeEach(() => {
vi.resetModules();
});
test('change state', async () => {
const mod = await import('./some/path.js'); // Будет переоценен
mod.changeLocalState('new value');
expect(mod.getLocalState()).toBe('new value');
});
test('module has old state', async () => {
const mod = await import('./some/path.js'); // Будет переоценен
expect(mod.getLocalState()).toBe('old value');
});
WARNING
Не сбрасывает реестр имитаций. Чтобы очистить реестр имитаций, используйте vi.unmock
или vi.doUnmock
.
vi.dynamicImportSettled
Дождитесь загрузки всех динамических импортов. Полезно, если у вас есть синхронный вызов, который начинает импортировать модуль, который вы не можете дождаться иным способом.
import { expect, test } from 'vitest';
// не может отслеживать импорт, потому что Promise не возвращается
function renderComponent() {
import('./component.js').then(({ render }) => {
render();
});
}
test('operations are resolved', async () => {
renderComponent();
await vi.dynamicImportSettled();
expect(document.querySelector('.component')).not.toBeNull();
});
TIP
Если во время динамического импорта инициируется другой динамический импорт, этот метод будет ждать, пока все они не будут разрешены.
Этот метод также будет ждать следующего тика setTimeout
после разрешения импорта, поэтому все синхронные операции должны быть завершены к моменту его разрешения.
Имитация функций и объектов
В этом разделе описывается, как работать с имитациями методов и заменять переменные среды и глобальные переменные.
vi.fn
- Тип:
(fn?: Function) => Mock
Создает шпионскую функцию, которую можно использовать как с существующей функцией, так и без нее. Каждый раз, когда вызывается функция, она сохраняет свои аргументы вызова, возвращаемые значения и экземпляры. Кроме того, вы можете управлять ее поведением с помощью методов. Если функция не задана, mock вернет undefined
при вызове.
import { expect, vi } from 'vitest';
// ---cut---
const getApples = vi.fn(() => 0);
getApples();
expect(getApples).toHaveBeenCalled();
expect(getApples).toHaveReturnedWith(0);
getApples.mockReturnValueOnce(5);
const res = getApples();
expect(res).toBe(5);
expect(getApples).toHaveNthReturnedWith(2, 5);
vi.isMockFunction
- Тип:
(fn: Function) => boolean
Проверяет, является ли данный параметр mock-функцией. Если вы используете TypeScript, он также сузит его тип.
vi.clearAllMocks
Вызовет .mockClear()
для всех шпионов. Это очистит историю мока, но не вернет его к исходной реализации.
vi.resetAllMocks
Вызовет .mockReset()
для всех шпионов. Это очистит историю мока и сбросит его реализацию до пустой функции (вернет undefined
).
vi.restoreAllMocks
Вызовет .mockRestore()
для всех шпионов. Это очистит историю мока и сбросит его реализацию до исходной.
vi.spyOn
- Тип:
<T, K extends keyof T>(object: T, method: K, accessType?: 'get' | 'set') => MockInstance
Создает шпион за методом или геттером/сеттером объекта, аналогично vi.fn()
. Он возвращает mock-функцию.
import { expect, vi } from 'vitest';
// ---cut---
let apples = 0;
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockImplementation(() => apples);
apples = 1;
expect(cart.getApples()).toBe(1);
expect(spy).toHaveBeenCalled();
expect(spy).toHaveReturnedWith(1);
TIP
Вы можете использовать vi.restoreAllMocks
внутри afterEach
(или включить test.restoreMocks
), чтобы восстановить все методы до их исходных реализаций. Это восстановит оригинальный дескриптор объекта, поэтому вы не сможете изменить реализацию метода:
const cart = {
getApples: () => 42,
};
const spy = vi.spyOn(cart, 'getApples').mockReturnValue(10);
console.log(cart.getApples()); // 10
vi.restoreAllMocks();
console.log(cart.getApples()); // 42
spy.mockReturnValue(10);
console.log(cart.getApples()); // все еще 42!
vi.stubEnv 0.26.0+
- Тип:
(name: string, value: string) => Vitest
Изменяет значение переменной среды в process.env
и import.meta.env
. Вы можете восстановить его значение, вызвав vi.unstubAllEnvs
.
import { vi } from 'vitest';
// `process.env.NODE_ENV` и `import.meta.env.NODE_ENV`
// имеют значение "development" перед вызовом "vi.stubEnv"
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
// не изменяет другие переменные среды
import.meta.env.MODE === 'development';
TIP
Вы также можете изменить значение, просто присвоив его, но вы не сможете использовать vi.unstubAllEnvs
для восстановления предыдущего значения:
import.meta.env.MODE = 'test';
vi.unstubAllEnvs 0.26.0+
- Тип:
() => Vitest
Восстанавливает все значения import.meta.env
и process.env
, которые были изменены с помощью vi.stubEnv
. При первом вызове Vitest запоминает исходное значение и сохраняет его, пока не будет вызван unstubAllEnvs
снова.
import { vi } from 'vitest';
// `process.env.NODE_ENV` и `import.meta.env.NODE_ENV`
// имеют значение "development" перед вызовом stubEnv
vi.stubEnv('NODE_ENV', 'production');
process.env.NODE_ENV === 'production';
import.meta.env.NODE_ENV === 'production';
vi.stubEnv('NODE_ENV', 'staging');
process.env.NODE_ENV === 'staging';
import.meta.env.NODE_ENV === 'staging';
vi.unstubAllEnvs();
// восстанавливает значение, которое было сохранено до первого вызова "stubEnv"
process.env.NODE_ENV === 'development';
import.meta.env.NODE_ENV === 'development';
vi.stubGlobal
- Тип:
(name: string | number | symbol, value: unknown) => Vitest
Изменяет значение глобальной переменной. Вы можете восстановить его исходное значение, вызвав vi.unstubAllGlobals
.
import { vi } from 'vitest';
// `innerWidth` имеет значение "0" перед вызовом stubGlobal
vi.stubGlobal('innerWidth', 100);
innerWidth === 100;
globalThis.innerWidth === 100;
// если вы используете jsdom или happy-dom
window.innerWidth === 100;
TIP
Вы также можете изменить значение, просто присвоив его globalThis
или window
(если вы используете среду jsdom
или happy-dom
), но вы не сможете использовать vi.unstubAllGlobals
для восстановления исходного значения:
globalThis.innerWidth = 100;
// если вы используете jsdom или happy-dom
window.innerWidth = 100;
vi.unstubAllGlobals 0.26.0+
- Тип:
() => Vitest
Восстанавливает все глобальные значения в globalThis
/global
(и window
/top
/self
/parent
, если вы используете среду jsdom
или happy-dom
), которые были изменены с помощью vi.stubGlobal
. При первом вызове Vitest запоминает исходное значение и сохраняет его, пока не будет вызван unstubAllGlobals
снова.
import { vi } from 'vitest';
const Mock = vi.fn();
// IntersectionObserver имеет значение "undefined" перед вызовом "stubGlobal"
vi.stubGlobal('IntersectionObserver', Mock);
IntersectionObserver === Mock;
global.IntersectionObserver === Mock;
globalThis.IntersectionObserver === Mock;
// если вы используете jsdom или happy-dom
window.IntersectionObserver === Mock;
vi.unstubAllGlobals();
globalThis.IntersectionObserver === undefined;
'IntersectionObserver' in globalThis === false;
// выдает ReferenceError, так как переменная не определена
IntersectionObserver === undefined;
Имитация таймеров
В этом разделе описывается, как работать с имитацией таймеров.
vi.advanceTimersByTime
- Type:
(ms: number) => Vitest
Этот метод продвигает все активные таймеры на указанное время, либо до тех пор, пока очередь таймеров не станет пустой, в зависимости от того, что произойдет раньше.
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersByTime(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersByTimeAsync
- Type:
(ms: number) => Promise<Vitest>
Этот метод асинхронно продвигает все активные таймеры на указанное время, либо до тех пор, пока очередь таймеров не станет пустой, в зависимости от того, что произойдет раньше. Включает асинхронные таймеры.
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersByTimeAsync(150);
// log: 1
// log: 2
// log: 3
vi.advanceTimersToNextTimer
- Type:
() => Vitest
Этот метод выполняет следующий доступный таймер. Удобно для проверок между вызовами таймеров. Можно вызывать последовательно для пошагового управления таймерами.
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.advanceTimersToNextTimer() // log: 1
.advanceTimersToNextTimer() // log: 2
.advanceTimersToNextTimer(); // log: 3
vi.advanceTimersToNextTimerAsync
- Type:
() => Promise<Vitest>
Этот метод выполняет следующий доступный таймер и ожидает его завершения, если он был установлен асинхронно. Удобно для проверок между вызовами таймеров.
import { expect, vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => Promise.resolve().then(() => console.log(++i)), 50);
await vi.advanceTimersToNextTimerAsync(); // log: 1
expect(console.log).toHaveBeenCalledWith(1);
await vi.advanceTimersToNextTimerAsync(); // log: 2
await vi.advanceTimersToNextTimerAsync(); // log: 3
vi.getTimerCount
- Type:
() => number
Возвращает количество ожидающих выполнения таймеров.
vi.clearAllTimers
Удаляет все таймеры, которые запланированы для запуска. Эти таймеры не будут запущены.
vi.getMockedSystemTime
- Type:
() => Date | null
Возвращает имитированное текущее время, которое было установлено с помощью setSystemTime
. Если время не имитировано, метод вернет null
.
vi.getRealSystemTime
- Type:
() => number
При использовании vi.useFakeTimers
вызовы Date.now
имитируются. Если вам необходимо получить реальное время в миллисекундах, вы можете вызвать эту функцию.
vi.runAllTicks
- Type:
() => Vitest
Выполняет каждую микрозадачу, добавленную в очередь process.nextTick
. Также запускает все микрозадачи, запланированные этими микрозадачами.
vi.runAllTimers
- Type:
() => Vitest
Этот метод выполняет все запущенные таймеры до тех пор, пока очередь таймеров не станет пустой. Это означает, что каждый таймер, вызванный в runAllTimers
, будет выполнен. При бесконечном интервале будет выброшена ошибка после 10 000 попыток (настраивается через fakeTimers.loopLimit
).
import { vi } from 'vitest';
// ---cut---
let i = 0;
setTimeout(() => console.log(++i));
const interval = setInterval(() => {
console.log(++i);
if (i === 3) clearInterval(interval);
}, 50);
vi.runAllTimers();
// log: 1
// log: 2
// log: 3
vi.runAllTimersAsync
- Type:
() => Promise<Vitest>
Этот метод асинхронно выполняет все запущенные таймеры до тех пор, пока очередь таймеров не станет пустой. Это означает, что каждый таймер, вызванный в runAllTimersAsync
, будет выполнен, включая асинхронные. При бесконечном интервале будет выброшена ошибка после 10 000 попыток (настраивается через fakeTimers.loopLimit
).
import { vi } from 'vitest';
// ---cut---
setTimeout(async () => {
console.log(await Promise.resolve('result'));
}, 100);
await vi.runAllTimersAsync();
// log: result
vi.runOnlyPendingTimers
- Type:
() => Vitest
Этот метод выполняет все таймеры, запущенные после вызова vi.useFakeTimers
. Таймеры, запущенные во время его выполнения, не вызываются.
import { vi } from 'vitest';
// ---cut---
let i = 0;
setInterval(() => console.log(++i), 50);
vi.runOnlyPendingTimers();
// log: 1
vi.runOnlyPendingTimersAsync
- Type:
() => Promise<Vitest>
Этот метод асинхронно выполняет все таймеры, запущенные после вызова vi.useFakeTimers
, включая асинхронные. Таймеры, запущенные во время его выполнения, не вызываются.
import { vi } from 'vitest';
// ---cut---
setTimeout(() => {
console.log(1);
}, 100);
setTimeout(() => {
Promise.resolve().then(() => {
console.log(2);
setInterval(() => {
console.log(3);
}, 40);
});
}, 10);
await vi.runOnlyPendingTimersAsync();
// log: 2
// log: 3
// log: 3
// log: 1
vi.setSystemTime
- Type:
(date: string | number | Date) => void
Если включена имитация таймеров, этот метод имитирует изменение системного времени пользователем (влияет на API, связанные с датой, такие как hrtime
, performance.now
или new Date()
), но не запускает таймеры. Если имитация таймеров выключена, метод имитирует только вызовы Date.*
.
Полезно, если вам нужно протестировать что-либо, что зависит от текущей даты, например, вызовы Luxon внутри вашего кода.
import { expect, vi } from 'vitest';
// ---cut---
const date = new Date(1998, 11, 19);
vi.useFakeTimers();
vi.setSystemTime(date);
expect(Date.now()).toBe(date.valueOf());
vi.useRealTimers();
vi.useFakeTimers
- Type:
(config?: FakeTimerInstallOpts) => Vitest
Чтобы включить имитацию таймеров, необходимо вызвать этот метод. Он перехватывает все последующие вызовы таймеров (такие как setTimeout
, setInterval
, clearTimeout
, clearInterval
, setImmediate
, clearImmediate
и Date
) до вызова vi.useRealTimers()
.
Имитация nextTick
не поддерживается при запуске Vitest внутри node:child_process
с использованием --pool=forks
. NodeJS использует process.nextTick
внутри node:child_process
и зависает, когда он имитируется. Имитация nextTick
поддерживается при запуске Vitest с --pool=threads
.
Реализация основана на @sinonjs/fake-timers
.
TIP
Начиная с версии 0.35.0
, vi.useFakeTimers()
больше не имитирует автоматически process.nextTick
. Его все еще можно имитировать, указав параметр toFake
в аргументе: vi.useFakeTimers({ toFake: ['nextTick'] })
.
vi.isFakeTimers 0.34.5+
- Type:
() => boolean
Возвращает true
, если имитация таймеров включена.
vi.useRealTimers
- Type:
() => Vitest
Чтобы отключить имитацию таймеров и вернуть оригинальные реализации, вызовите этот метод. Все ранее запланированные таймеры будут отменены.
Разное
Набор полезных вспомогательных функций, которые предоставляет Vitest.
vi.waitFor 0.34.5+
- Type:
<T>(callback: WaitForCallback<T>, options?: number | WaitForOptions) => Promise<T>
Ожидает успешного выполнения обратного вызова. Если обратный вызов выдает ошибку или возвращает отклоненный промис, ожидание продолжается до успешного завершения или истечения времени ожидания.
Это очень полезно, когда вам нужно дождаться завершения некоторого асинхронного действия, например, когда вы запускаете сервер и вам нужно дождаться его запуска.
import { expect, test, vi } from 'vitest';
import { createServer } from './server.js';
test('Server started successfully', async () => {
const server = createServer();
await vi.waitFor(
() => {
if (!server.isReady) throw new Error('Server not started');
console.log('Server started');
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
);
expect(server.isReady).toBe(true);
});
// @vitest-environment jsdom
import { expect, test, vi } from 'vitest';
import { getDOMElementAsync, populateDOMAsync } from './dom.js';
test('Element exists in a DOM', async () => {
// start populating DOM (начать заполнение DOM)
populateDOMAsync();
const element = await vi.waitFor(
async () => {
// try to get the element until it exists (попытка получить элемент, пока он не появится)
const element = (await getDOMElementAsync()) as HTMLElement | null;
expect(element).toBeTruthy();
expect(element.dataset.initialized).toBeTruthy();
return element;
},
{
timeout: 500, // default is 1000
interval: 20, // default is 50
}
);
expect(element).toBeInstanceOf(HTMLElement);
});
Если используется vi.useFakeTimers
, vi.waitFor
автоматически вызывает vi.advanceTimersByTime(interval)
в каждом обратном вызове проверки.
vi.waitUntil 0.34.5+
- Type:
<T>(callback: WaitUntilCallback<T>, options?: number | WaitUntilOptions) => Promise<T>
Аналогично vi.waitFor
, но при возникновении ошибки в обратном вызове выполнение немедленно прерывается и возвращается сообщение об ошибке. Если обратный вызов возвращает ложное значение, следующая проверка выполняется до тех пор, пока не будет возвращено истинное значение. Удобно, когда необходимо дождаться существования чего-либо перед следующим шагом.
Посмотрите на пример ниже. Мы можем использовать vi.waitUntil
, чтобы дождаться появления элемента на странице, а затем мы можем что-то сделать с элементом.
import { expect, test, vi } from 'vitest';
test('Element render correctly', async () => {
const element = await vi.waitUntil(() => document.querySelector('.element'), {
timeout: 500, // default is 1000
interval: 20, // default is 50
});
// do something with the element (сделать что-то с элементом)
expect(element.querySelector('.element-child')).toBeTruthy();
});
vi.hoisted 0.31.0+
- Type:
<T>(factory: () => T) => T
Все статические операторы import
в ES-модулях перемещаются в начало файла, поэтому код, определенный до импорта, выполняется после обработки импорта.
Однако иногда требуется выполнить побочные эффекты (например, имитацию дат) до импорта модуля.
Чтобы обойти это ограничение, вы можете переписать статические импорты в динамические, например так:
callFunctionWithSideEffect()
- import { value } from './some/module.js'
+ const { value } = await import('./some/module.js')
При запуске vitest
вы можете сделать это автоматически, используя метод vi.hoisted
.
- callFunctionWithSideEffect()
import { value } from './some/module.js'
+ vi.hoisted(() => callFunctionWithSideEffect())
Этот метод возвращает результат работы фабричной функции. Вы можете использовать это значение в своих фабриках vi.mock
, если вам нужен легкий доступ к локально определенным переменным:
import { expect, vi } from 'vitest';
import { originalMethod } from './path/to/module.js';
const { mockedMethod } = vi.hoisted(() => {
return { mockedMethod: vi.fn() };
});
vi.mock('./path/to/module.js', () => {
return { originalMethod: mockedMethod };
});
mockedMethod.mockReturnValue(100);
expect(originalMethod()).toBe(100);
Обратите внимание, что этот метод также можно вызывать асинхронно, даже если ваша среда не поддерживает ожидание верхнего уровня:
const promised = await vi.hoisted(async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
return response.json();
});
vi.setConfig
- Type:
RuntimeConfig
Обновляет конфигурацию для текущего тестового файла. Поддерживаются только параметры, влияющие на текущий тестовый файл.
vi.setConfig({
allowOnly: true,
testTimeout: 10_000,
hookTimeout: 10_000,
clearMocks: true,
restoreMocks: true,
fakeTimers: {
now: new Date(2021, 11, 19),
// supports the whole object (поддерживает весь объект)
},
maxConcurrency: 10,
sequence: {
hooks: 'stack',
// supports only "sequence.hooks" (поддерживает только "sequence.hooks")
},
});
vi.resetConfig
- Type:
RuntimeConfig
Отменяет все изменения конфигурации, сделанные через vi.setConfig
.