Руководство по миграции
Миграция на Vitest 2.0
Пул по умолчанию теперь forks
Vitest 2.0 изменяет конфигурацию pool
по умолчанию на 'forks'
для повышения стабильности. Полное обоснование можно найти в PR.
Если вы использовали poolOptions
без указания pool
, возможно, вам потребуется обновить конфигурацию:
export default defineConfig({
test: {
poolOptions: {
threads: {
singleThread: true,
},
forks: {
singleFork: true,
},
},
},
});
Хуки выполняются последовательно
До Vitest 2.0 все хуки выполнялись параллельно. В версии 2.0 все хуки выполняются последовательно. Кроме того, хуки afterAll
/afterEach
выполняются в обратном порядке.
Чтобы вернуться к параллельному выполнению хуков, измените sequence.hooks
на 'parallel'
:
export default defineConfig({
test: {
sequence: {
hooks: 'parallel',
},
},
});
suite.concurrent
запускает все тесты параллельно
Ранее, если для набора тестов указывался concurrent
, параллельные тесты группировались по наборам и запускались последовательно. Теперь, следуя поведению Jest, все тесты запускаются параллельно (с учетом ограничений maxConcurrency
).
coverage.ignoreEmptyLines
для V8 Coverage включен по умолчанию
Значение по умолчанию для coverage.ignoreEmptyLines
теперь true
. Это важное изменение может повлиять на отчеты о покрытии кода и потребовать корректировки пороговых значений покрытия для некоторых проектов. Эта корректировка влияет только на настройку по умолчанию, когда coverage.provider
равен 'v8'
.
Удаление опции watchExclude
Vitest использует наблюдатель Vite. Исключайте файлы или каталоги, добавляя их в server.watch.ignored
:
export default defineConfig({
server: {
watch: {
ignored: ['!node_modules/examplejs'],
},
},
});
--segfault-retry
удален
С изменениями в пуле по умолчанию эта опция больше не нужна. Если вы столкнулись с ошибками сегментации (segfault), попробуйте переключиться на пул 'forks'
. Если проблема сохраняется, пожалуйста, откройте новую проблему с воспроизведением.
Пустая задача в задачах набора удалена
Это изменение касается расширенного API задач. Ранее, при обходе .suite
, это в конечном итоге приводило к пустой внутренней задаче, которая использовалась вместо файловой задачи.
Это делает .suite
необязательным; если задача определена на верхнем уровне, она не будет иметь набора. Вы можете вернуться к свойству .file
, которое теперь присутствует во всех задачах (включая саму файловую задачу, поэтому будьте осторожны, чтобы не попасть в бесконечную рекурсию).
Это изменение также удаляет файл из expect.getState().currentTestName
и делает expect.getState().testPath
обязательным для использования.
task.meta
добавлен в JSON Reporter
JSON-репортер теперь включает task.meta
для каждого результата утверждения.
Упрощенные обобщенные типы мок-функций (например, vi.fn<T>
, Mock<T>
)
Ранее vi.fn<TArgs, TReturn>
принимал два обобщенных типа: отдельно для аргументов и для возвращаемого значения. Теперь он напрямую принимает тип функции vi.fn<T>
для упрощения использования.
import { type Mock, vi } from 'vitest';
const add = (x: number, y: number): number => x + y;
// using vi.fn<T>
const mockAdd = vi.fn<Parameters<typeof add>, ReturnType<typeof add>>();
const mockAdd = vi.fn<typeof add>();
// using Mock<T>
const mockAdd: Mock<Parameters<typeof add>, ReturnType<typeof add>> = vi.fn();
const mockAdd: Mock<typeof add> = vi.fn();
Доступ к разрешенным mock.results
Ранее Vitest автоматически разрешал промисы, возвращаемые функцией, и их значения записывались в mock.results
. Теперь есть отдельное свойство mock.settledResults
, которое заполняется только тогда, когда возвращенный Promise разрешен или отклонен.
const fn = vi.fn().mockResolvedValueOnce('result');
await fn();
const result = fn.mock.results[0]; // 'result'
const result = fn.mock.results[0]; // 'Promise<result>'
const settledResult = fn.mock.settledResults[0]; // 'result'
С этим изменением мы также вводим новые матчеры toHaveResolved*
, аналогичные toHaveReturned
, чтобы упростить миграцию, если вы использовали toHaveReturned
раньше:
const fn = vi.fn().mockResolvedValueOnce('result');
await fn();
expect(fn).toHaveReturned('result');
expect(fn).toHaveResolved('result');
Режим браузера
Режим браузера Vitest претерпел множество изменений во время бета-цикла. Вы можете прочитать о нашей философии в отношении режима браузера на странице обсуждения GitHub.
Большинство изменений были связаны с добавлением новых функций, но также присутствовали и небольшие обратно несовместимые изменения:
- Провайдер
none
был переименован вpreview
#5842 - Провайдер
preview
теперь является провайдером по умолчанию #5842 indexScripts
переименован вorchestratorScripts
#5842
Удалены устаревшие опции
Некоторые устаревшие параметры были удалены:
- Команда
vitest typecheck
- вместо нее используйтеvitest --typecheck
- Переменные окружения
VITEST_JUNIT_CLASSNAME
иVITEST_JUNIT_SUITE_NAME
(вместо них используйте опции репортера) - Проверка покрытия
c8
(вместо нее используйте coverage-v8) - Экспорт
SnapshotEnvironment
изvitest
- импортируйте его изvitest/snapshot
SpyInstance
удален в пользуMockInstance
Миграция на Vitest 1.0
Минимальные требования
Vitest 1.0 требует Vite 5.0 и Node.js 18 или выше.
Для всех подпакетов @vitest/*
требуется Vitest версии 1.0.
Обновление снимков #3961
Кавычки в снимках больше не экранируются, и все снимки используют обратные кавычки (`) даже для однострочных значений.
- Кавычки больше не экранируются:
expect({ foo: 'bar' }).toMatchInlineSnapshot(`
Object {
- \\"foo\\": \\"bar\\",
+ "foo": "bar",
}
`)
- Однострочные снимки теперь используют кавычки "`" вместо ':
- expect('some string').toMatchInlineSnapshot('"some string"')
+ expect('some string').toMatchInlineSnapshot(`"some string"`)
Также были изменения в пакете @vitest/snapshot
. Если вы не используете его напрямую, вам ничего не нужно менять.
- Вам больше не нужно расширять
SnapshotClient
только для переопределения методаequalityCheck
: просто передайте его какisEqual
при инициализации экземпляра. client.setTest
был переименован вclient.startCurrentRun
client.resetCurrent
был переименован вclient.finishCurrentRun
Пулы стандартизированы #4172
Мы удалили множество параметров конфигурации, чтобы упростить настройку тестового фреймворка под ваши нужды. Пожалуйста, ознакомьтесь с примерами миграции, если вы полагаетесь на --threads
или другие связанные флаги.
--threads
теперь--pool=threads
--no-threads
теперь--pool=forks
--single-thread
теперь--poolOptions.threads.singleThread
--experimental-vm-threads
теперь--pool=vmThreads
--experimental-vm-worker-memory-limit
теперь--poolOptions.vmThreads.memoryLimit
--isolate
теперь--poolOptions.<pool-name>.isolate
иbrowser.isolate
test.maxThreads
теперьtest.poolOptions.<pool-name>.maxThreads
test.minThreads
теперьtest.poolOptions.<pool-name>.minThreads
test.useAtomics
теперьtest.poolOptions.<pool-name>.useAtomics
test.poolMatchGlobs.child_process
теперьtest.poolMatchGlobs.forks
test.poolMatchGlobs.experimentalVmThreads
теперьtest.poolMatchGlobs.vmThreads
{
scripts: {
- "test": "vitest --no-threads"
// For identical behaviour:
+ "test": "vitest --pool forks --poolOptions.forks.singleFork"
// Or multi parallel forks:
+ "test": "vitest --pool forks"
}
}
{
scripts: {
- "test": "vitest --experimental-vm-threads"
+ "test": "vitest --pool vmThreads"
}
}
{
scripts: {
- "test": "vitest --isolate false"
+ "test": "vitest --poolOptions.threads.isolate false"
}
}
{
scripts: {
- "test": "vitest --no-threads --isolate false"
+ "test": "vitest --pool forks --poolOptions.forks.isolate false"
}
}
Изменения в покрытии #4265, #4442
Опция coverage.all
теперь включена по умолчанию. Это означает, что все файлы проекта, соответствующие шаблону coverage.include
, будут обработаны, даже если они не были выполнены.
Форма API пороговых значений покрытия была изменена, и теперь она поддерживает указание пороговых значений для конкретных файлов с использованием шаблонов glob:
export default defineConfig({
test: {
coverage: {
- perFile: true,
- thresholdAutoUpdate: true,
- 100: true,
- lines: 100,
- functions: 100,
- branches: 100,
- statements: 100,
+ thresholds: {
+ perFile: true,
+ autoUpdate: true,
+ 100: true,
+ lines: 100,
+ functions: 100,
+ branches: 100,
+ statements: 100,
+ }
}
}
})
Типы моков #4400
Несколько типов были удалены в пользу именования "Mock", соответствующего стилю Jest.
- import { EnhancedSpy, SpyInstance } from 'vitest'
+ import { MockInstance } from 'vitest'
WARNING
SpyInstance
устарел в пользу MockInstance
и будет удален в следующем основном выпуске.
Моки таймеров #3925
vi.useFakeTimers()
больше не мокает автоматически process.nextTick
. По-прежнему можно мокать process.nextTick
, явно указав его с помощью vi.useFakeTimers({ toFake: ['nextTick'] })
.
Однако мокинг process.nextTick
невозможен при использовании --pool=forks
. Используйте другую опцию --pool
, если вам нужен мокинг process.nextTick
.
Миграция с Jest
Vitest был разработан с API, совместимым с Jest, чтобы максимально упростить миграцию с Jest. Несмотря на эти усилия, вы все равно можете столкнуться со следующими различиями:
Глобальные объекты по умолчанию
Jest по умолчанию включает свой глобальный API. Vitest не включает их по умолчанию. Вы можете либо включить глобальные переменные через настройку конфигурации globals
, либо обновить свой код для использования импортов из модуля vitest
.
Если вы решите оставить глобальные переменные отключенными, имейте в виду, что популярные библиотеки, такие как testing-library
, не будут выполнять автоматическую очистку DOM.
Моки модулей
При создании мока модуля в Jest возвращаемое значение фабричной функции является экспортом по умолчанию. В Vitest фабричная функция должна возвращать объект, в котором явно определен каждый экспорт. Например, следующий jest.mock
должен быть обновлен следующим образом:
jest.mock('./some-path', () => 'hello');
vi.mock('./some-path', () => ({
default: 'hello',
}));
Для получения более подробной информации, пожалуйста, обратитесь к разделу API vi.mock
.
Поведение автоматического мокирования
В отличие от Jest, мок-модули в <root>/__mocks__
не загружаются, если не вызван vi.mock()
. Если вам нужно, чтобы они были мокированы в каждом тесте, как в Jest, вы можете мокировать их внутри setupFiles
.
Импорт оригинальной версии мокированного пакета
Если вы выполняете частичное мокирование пакета, возможно, ранее вы использовали функцию Jest requireActual
. В Vitest вам следует заменить эти вызовы на vi.importActual
.
const { cloneDeep } = jest.requireActual('lodash/cloneDeep');
const { cloneDeep } = await vi.importActual('lodash/cloneDeep');
Расширение мокирования на внешние библиотеки
В отличие от Jest, который делает это по умолчанию, при мокировании модуля и необходимости распространить это мокирование на другие внешние библиотеки, использующие тот же модуль, вам следует явно указать, какую стороннюю библиотеку вы хотите мокировать. Для этого используйте server.deps.inline
, чтобы внешняя библиотека обрабатывалась как часть вашего исходного кода.
server.deps.inline: ["lib-name"]
expect.getState().currentTestName
Имена test
в Vitest объединяются символом >
для лучшего различения тестов от наборов, тогда как Jest использует пробел ().
- `${describeTitle} ${testTitle}`
+ `${describeTitle} > ${testTitle}`
Переменные окружения
Как и Jest, Vitest устанавливает NODE_ENV
в test
, если он не был установлен ранее. Vitest также имеет аналог JEST_WORKER_ID
, который называется VITEST_POOL_ID
(всегда меньше или равен maxThreads
). Если вы используете JEST_WORKER_ID
, не забудьте переименовать его. Vitest также предоставляет VITEST_WORKER_ID
, который является уникальным идентификатором запущенного рабочего процесса. Это число не зависит от maxThreads
и увеличивается с каждым созданным рабочим процессом.
Свойство replace
Если вы хотите изменить объект, в Jest вы бы использовали API replaceProperty
. В Vitest для этой цели можно использовать vi.stubEnv
или vi.spyOn
.
Колбэк Done
Начиная с Vitest v0.10.0, стиль объявления тестов с использованием функций обратного вызова устарел. Вы можете переписать их, используя функции async
/await
, или использовать Promise для эмуляции поведения функций обратного вызова.
it('should work', (done) => { // [!code --]
it('should work', () => new Promise(done => { // [!code ++]
// ...
done()
}) // [!code --]
})) // [!code ++]
Хуки
Хуки beforeAll
/beforeEach
могут возвращать функцию завершения в Vitest. Из-за этого вам может потребоваться переписать ваши хуки, если они возвращают что-либо, кроме undefined
или null
:
beforeEach(() => setActivePinia(createTestingPinia()));
beforeEach(() => {
setActivePinia(createTestingPinia());
});
В Jest хуки вызываются последовательно. По умолчанию Vitest запускает хуки параллельно. Чтобы использовать поведение Jest, обновите опцию sequence.hooks
:
export default defineConfig({
test: {
sequence: {
hooks: 'list',
},
},
});
Типы
Vitest не имеет эквивалента глобального объекта jest
, поэтому вам нужно будет импортировать типы непосредственно из vitest
:
let fn: jest.Mock<(name: string) => number>;
import type { Mock } from 'vitest';
let fn: Mock<(name: string) => number>;
Таймеры
Vitest не поддерживает устаревший механизм таймеров Jest.
Таймаут
Если вы использовали jest.setTimeout
, вам нужно будет перейти на vi.setConfig
:
jest.setTimeout(5_000);
vi.setConfig({ testTimeout: 5_000 });
Снимки Vue
Это не специфичная для Jest функция, но если вы ранее использовали Jest с пресетом vue-cli, вам потребуется установить пакет jest-serializer-vue
и использовать его в setupFiles:
import { defineConfig } from 'vite';
export default defineConfig({
test: {
setupFiles: ['./tests/unit/setup.js'],
},
});
import vueSnapshotSerializer from 'jest-serializer-vue';
expect.addSnapshotSerializer(vueSnapshotSerializer);
В противном случае ваши снимки будут содержать много экранированных символов "
.