Что изменилось в Vitest 5
Разберитесь в изменениях Vitest 5: приросте производительности, несовместимых изменениях, настройках по умолчанию, путях отчетов и шагах обновления тестов и CI.
Vitest 5.0 вышел 3 сентября 2026 года. Это мажорный релиз с упором на производительность. Он требует Node.js 22.12.0+ и Vite 6.4.0+, включает очистку моков по умолчанию, роняет тесты с асинхронными проверками без await и переносит вывод репортеров в единую директорию .vitest/.
Большинство ошибок после обновления исправляются легко. Сложнее с тестами, которые локально проходят на Vitest 4, а в CI падают с ошибкой, ничего не говорящей о причине.
В этой статье изменения из release notes v5.0.0 разобраны по степени влияния. Сначала о том, что стало быстрее. Затем об изменениях, требующих правки кода, и об изменениях, которые незаметно меняют пути и правила сопоставления. В конце — чек-лист обновления и итоговая рекомендация.
Ключевые выводы
- Vitest 5.0 требует Node.js 22.12.0 или новее и Vite 6.4.0 или новее.
- По бенчмаркам команды Vitest, большинство протестированных конфигураций работают на 8–25 % быстрее, а некоторые конфигурации с VM-пулами — до 53 % быстрее. Там, где основное время уходит на подготовку окружения, прирост почти незаметен.
clearMocksтеперь по умолчанию равенtrue. Проверка истории вызовов, записанной в setup-файле, хукеbeforeAllили предыдущем тесте, теперь видит ноль вызовов.- Blob-отчёты, вложения (attachments), а также вывод репортеров JSON, JUnit и HTML по умолчанию сохраняются в
.vitest/, поэтому шаги CI, загружающие артефакты, нужно обновить. toThrow('')теперь совпадает с любой выброшенной ошибкой, поэтому для проверки именно пустого сообщения нужен явный шаблон.
Почему Vitest 5 работает быстрее?
Согласно анонсу Vitest 5, большинство конфигураций в собственных бенчмарках команды Vitest работают на 8–25 % быстрее. Больше всего выигрывают VM-пулы: в некоторых конфигурациях — до 53 %. Ускоряется не всё: прогоны, где основное время уходит на создание тестового окружения (например, forks с jsdom и изоляцией), укладываются в пределы 3 % от Vitest 4.1. Цифры получены в репозитории vitest-dev/benchmarks, где команда сгенерировала тестовые приложения разного размера — от небольшого пакета из 5 файлов до монолита из 1280 модулей. В анонсе от VoidZero это округлено до «VM-пулы до 53 % быстрее, прирост ~18 % по всем направлениям, включая Browser Mode».
Согласно release notes, основной прирост дают четыре изменения:
- Общий Vite-сервер. Inline-проекты теперь используют один Vite-сервер вместо того, чтобы каждый запускал собственный.
fsModuleCache. Теперь это опция верхнего уровня. Она сохраняет трансформированные модули на диск, поэтому повторный запуск или другой процесс Vitest может пропустить эту работу.- Меньше обменов данными. Уже трансформированные модули теперь попадают в воркер из основного процесса за один проход.
- Переиспользование в VM-пулах. Пулы
vmThreadsиvmForksразделяют скомпилированный код между контекстами и заранее загружают граф модулей.
Vitest также поддерживает дисковый кеш компиляции Node, но его нужно включать явно.
Какие изменения в Vitest 5 требуют правки кода?
Шесть изменений в Vitest 5 приводят к падению тестов или ошибке конфигурации уже при первом запуске. Каждое из них описано в руководстве по миграции.
| Изменение | Симптом при первом запуске | Исправление |
|---|---|---|
clearMocks: true по умолчанию | Проверки количества вызовов видят 0 | Вызывайте мок внутри теста с проверкой или задайте clearMocks: false |
Асинхронная проверка без await | Тест падает | Добавить await |
Поднимаемый (hoisted) вызов vi не на верхнем уровне | Выбрасывается ошибка | Перенести на уровень модуля |
Удалён sequential | API удалён | { concurrent: false } |
| Нет поиска конфига в родительских директориях | Конфиг не найден | Добавить конфиг в папку пакета |
| Переписан API бенчмарков | Старый код бенчмарков ломается | Перейти на модель с фикстурами |
Очистка моков и hoisting
В Vitest 5 clearMocks по умолчанию равен true, поэтому vi.clearAllMocks() выполняется перед каждым тестом. История вызовов, записанная в setup-файле, хуке beforeAll или предыдущем тесте, стирается до того, как следующий тест её проверит. Реализации моков при этом сохраняются.
// Vitest 5.0.x
const track = vi.fn()
beforeAll(() => initAnalytics(track))
it('tracks once on init', () => {
expect(track).toHaveBeenCalledTimes(1) // now receives 0 calls
})
Исправление — выполнять вызов внутри того же теста, который его проверяет. Параметр clearMocks: false в секции test конфига возвращает прежнее поведение на время аудита тестов.
Вызов vi.mock или любого другого поднимаемого вызова vi где-либо, кроме верхнего уровня файла, теперь выбрасывает ошибку. Vitest в любом случае поднимает такие вызовы в начало модуля, так что код внутри блока describe никогда не выполнялся там, где был написан.
// Vitest 5.0.x: throws
describe('UserCard', () => {
const fetchUser = vi.fn()
vi.mock('./api', () => ({ fetchUser }))
})
// Vitest 5.0.x: works
const { fetchUser } = vi.hoisted(() => ({ fetchUser: vi.fn() }))
vi.mock('./api', () => ({ fetchUser }))
describe('UserCard', () => {
it('renders the user', async () => {
fetchUser.mockResolvedValue({ name: 'Ada' })
// mount and assert
})
})
Если в ваших Vue-тестах мокается слой API, тот же паттерн с верхним уровнем применим и к мокированию API-вызовов в Vue-тестах с Vitest.
Проверки без await
Тест, в котором асинхронная проверка осталась без await, теперь падает. Пропущенный await перед expect(...).resolves или .rejects делает тест красным.
// Vitest 5.0.x
test('loads config', async () => {
expect(loadConfig()).resolves.toEqual({ ok: true }) // fails
await expect(loadConfig()).resolves.toEqual({ ok: true }) // passes
})
Эта команда grep выводит список кандидатов. Каждое совпадение всё равно нужно проверить на наличие await в начале:
grep -rnE "expect\(.*\)\.(resolves|rejects)" src/ --include='*.test.*' --include='*.spec.*'
Конкурентность, поиск конфига и бенчмарки
Опции sequential для тестов и наборов тестов удалены. Чтобы отключить конкурентное выполнение, используйте test('example', { concurrent: false }, ...) или describe('suite', { concurrent: false }, ...).
Vitest 5 больше не ищет файл конфигурации в директориях выше текущей. Если вы запускаете vitest из подпапки пакета, в этой папке должен быть собственный конфиг.
API бенчмарков переписан. Теперь bench не импортируется в начале файла — вместо этого он берётся из контекста теста внутри обычного вызова test() в файле бенчмарка.
В release notes перечислены и другие ломающие изменения — проверьте, касаются ли они вас:
expect.pollтеперь падает по истечении тайм-аута.- Удалены устаревшие точки входа (entry points).
@vitest/runnerобъявлен устаревшим, аvitestбольше не зависит от@vitest/expect, поскольку код проверок теперь поставляется внутри самогоvitest.- Провайдер
@vitest/browser-webdriverioпереехал в организацию vitest-community и теперь поддерживается сообществом. workerIdтеперь нумеруется с 1.
toThrow('') теперь совпадает с любой выброшенной ошибкой. Если нужно проверить именно пустое сообщение, передайте регулярное выражение, например /^$/.
Что незаметно меняется в Vitest 5?
Шесть изменений в Vitest 5 не выбрасывают ошибок. Вместо этого под вашей текущей конфигурацией меняется путь, фильтр или результат сопоставления.
- Пути вывода. Blob-отчёты и
--merge-reportsпо умолчанию используют.vitest/blob/. Вложения переехали из.vitest-attachements/в.vitest/attachments/. Файлы репортеров JSON, JUnit и HTML тоже по умолчанию сохраняются в.vitest. - Фильтры
-t. Разделителем в фильтрах по имени теста теперь служит>. Проверьте CI-скрипты, которые фильтруют тесты по пути набора. - Локаторы в браузере. В Browser Mode
locators.exactтеперь включён по умолчанию. - Сопоставление текста.
toHaveTextContentтеперь работает строго. Новая альтернатива —toMatchTextContent. - Glob-шаблоны покрытия. Шаблоны
includeиexcludeтеперь сопоставляются с путём каждого файла относительно корня проекта, а шаблон без wildcard-символов считается целой папкой. Набор файлов, учитываемых в покрытии, может измениться, поэтому после первого запуска проверьте пороговые значения (thresholds). - Inline-проекты. Inline-проекты теперь наследуют корневой конфиг так, как будто задано
extends: true.
Типичный шаг загрузки артефактов меняется так:
- path: .vitest-attachements/
+ path: .vitest/attachments/
+ # sharded runs: upload .vitest/blob/ for --merge-reports
Что нового в Vitest 5 стоит знать
vi.when позволяет задать спаю разный результат для каждого набора аргументов. calledWith принимает асимметричные матчеры, а вызовы с аргументами, которые ни с чем не совпали, передаются в исходную реализацию.
// Vitest 5.0.x
vi.when(getUser).calledWith(1).thenResolve({ id: 1, name: 'Ada' })
В Browser Mode параметр test.browser.traceView: true включает режим просмотра трассировки (trace view). Каждое взаимодействие, проверка и вызов page.mark сохраняются как снимок DOM, так что тест можно пошагово воспроизвести в UI.
Теперь поддерживаются вложенные проекты, что помогает группировать связанные проекты в монорепозиториях.
Чек-лист обновления до Vitest 5
- Переведите CI и локальные окружения на Node.js 22.12.0+ и Vite 6.4.0+.
- Запустите приведённую выше команду grep и добавьте
awaitвезде, где он отсутствует. - Перенесите все вызовы
vi.mockиvi.hoistedна верхний уровень файла. - Замените
sequentialна{ concurrent: false }и добавьте конфиги в папки пакетов, которые полагались на родительский конфиг. - Запустите тесты. Если падают проверки количества вызовов, исправьте их или временно задайте
clearMocks: false. - Обновите пути к артефактам в CI на
.vitest/и проверьте фильтры-tи пороговые значения покрытия.
Обновляться до Vitest 5 сейчас или подождать?
Обновляйтесь до Vitest 5 в текущем спринте, если ваш CI уже работает на Node.js 22.12.0+ и Vite 6.4.0+. Большинство необходимых правок механические.
Исключение — наборы тестов, которые проверяют историю вызовов моков, переносимую между тестами: из setup-файлов, хуков beforeAll или когда один тест полагается на вызовы из другого. Такие падения никак не указывают на причину, поэтому сначала проведите аудит этих проверок, а затем обновляйтесь. В наборах тестов компонентов также стоит перезапустить проверки текста и локаторов; паттерны из статьи о тестировании компонентов Svelte 5 с Vitest показывают, где они обычно встречаются.
Vitest 5 быстрее, а большая часть того, что он ломает, — это тестовый код, который и так был некорректным. Начните в отдельной ветке с grep и переноса vi.mock, проверьте пути к артефактам в CI, а остальное подскажет первый прогон в CI.
Часто задаваемые вопросы
Включает ли Vitest 5 по умолчанию также mockReset или restoreMocks?
Нет. Согласно руководству по миграции на Vitest 5, значение по умолчанию изменено только для clearMocks. clearMocks вызывает vi.clearAllMocks() перед каждым тестом и сбрасывает mock.calls, mock.instances, mock.contexts и mock.results, но сохраняет реализации. mockReset идёт дальше: он очищает историю и возвращает каждую реализацию к исходной, так что мок, созданный через vi.fn(impl), снова использует impl. restoreMocks восстанавливает исходные реализации спаев, созданных через vi.spyOn.
Почему после обновления до Vitest 5 мой фильтр -t находит меньше тестов?
В Vitest 5 testNamePattern (флаг -t) сопоставляется с полным именем теста, которое строится через ' > ' между названием каждого набора и названием теста. Это тот же текст, что вы видите в выводе репортера. Vitest 4, как и Jest, использовал между частями одиночный пробел. Шаблон ломается, только если он переходит из одной части имени в другую. Чтобы это исправить, сопоставляйте только одну часть, например -t adds, или поставьте wildcard между частями, например -t 'math.*adds'.
Почему Vitest 5 не может найти vite после обновления через Yarn?
В Vitest 5 vite перестал быть прямой зависимостью и стал обязательной peer-зависимостью, поэтому Vitest работает с той версией Vite, которая установлена в вашем проекте. npm, pnpm, Bun и Deno добавляют peer-зависимости автоматически, а Yarn оставляет этот шаг вам. Добавьте vite версии 6.4.0 или новее в package.json и переустановите зависимости — после этого Vitest снова сможет его найти.
Как объединить отчёты шардированных тестов в Vitest 5?
Запустите каждый шард с blob-репортером, например vitest run --reporter=blob --shard=1/3 на первой машине. По умолчанию каждый шард записывает результаты в .vitest/blob/, а флаг --outputFile.blob позволяет изменить это расположение. Скопируйте директорию с каждой машины в одну финальную задачу и запустите vitest --merge-reports. Если ваши тесты сохраняют вложения в виде файлов, передайте в задачу объединения и папку с вложениями.
Gain Debugging Superpowers
Unleash the power of session replay to reproduce bugs, track slowdowns and uncover frustrations in your app. Get complete visibility into your frontend with OpenReplay — the most advanced open-source session replay tool for developers.
Star on GitHub12k