Руководство по миграции
Миграция на 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.startCurrentRunclient.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.isolatetest.maxThreadsтеперьtest.poolOptions.<pool-name>.maxThreadstest.minThreadsтеперьtest.poolOptions.<pool-name>.minThreadstest.useAtomicsтеперьtest.poolOptions.<pool-name>.useAtomicstest.poolMatchGlobs.child_processтеперьtest.poolMatchGlobs.forkstest.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) => {
it('should work', () => new Promise(done => {
// ...
done()
})
})) Хуки
Хуки 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);В противном случае ваши снимки будут содержать много экранированных символов ".