Руководство по миграции
Миграция на Vitest 3.0
Параметры теста как третий аргумент
Vitest 3.0 выводит предупреждение, если вы передаете объект в качестве третьего аргумента функциям test
или describe
:
test('validation works', () => {
// ...
}, { retry: 3 });
test('validation works', { retry: 3 }, () => {
// ...
});
В следующей основной версии будет генерироваться ошибка, если третий аргумент является объектом. Обратите внимание, что таймаут в числовом формате остается поддерживаемым:
test('validation works', () => {
// ...
}, 1000); // Ok ✅
browser.name
и browser.providerOptions
устарели
Обе опции browser.name
и browser.providerOptions
будут удалены в Vitest 4. Вместо них следует использовать новую опцию browser.instances
:
export default defineConfig({
test: {
browser: {
name: 'chromium',
providerOptions: {
launch: { devtools: true },
},
instances: [
{
browser: 'chromium',
launch: { devtools: true },
},
],
},
},
})
С новым полем browser.instances
вы также можете указать несколько конфигураций браузера.
spy.mockReset
теперь восстанавливает исходную реализацию
Раньше не существовало удобного способа сбросить мок до его исходной реализации без повторного применения шпиона. Теперь spy.mockReset
сбрасывает функцию реализации на исходную, а не на фиктивную noop.
const foo = {
bar: () => 'Hello, world!',
};
vi.spyOn(foo, 'bar').mockImplementation(() => 'Hello, mock!');
foo.bar(); // 'Hello, mock!'
foo.bar.mockReset();
foo.bar(); // undefined
foo.bar(); // 'Hello, world!'
vi.spyOn
повторно использует мок, если метод уже замокан
Ранее Vitest всегда назначал новый шпион при создании шпиона для объекта. Это приводило к ошибкам с mockRestore
, потому что он восстанавливал шпиона до предыдущего шпиона вместо исходной функции:
vi.spyOn(fooService, 'foo').mockImplementation(() => 'bar');
vi.spyOn(fooService, 'foo').mockImplementation(() => 'bar');
vi.restoreAllMocks();
vi.isMockFunction(fooService.foo); // true
vi.isMockFunction(fooService.foo); // false
Поведение фиктивных таймеров по умолчанию
Vitest больше не предоставляет опции fakeTimers.toFake
по умолчанию. Теперь Vitest будет имитировать любой API, связанный с таймерами, если он доступен (кроме nextTick
). В частности, performance.now()
теперь имитируется при вызове vi.useFakeTimers
.
vi.useFakeTimers();
performance.now(); // original
performance.now(); // fake
Вы можете вернуться к предыдущему поведению, указав таймеры при вызове vi.useFakeTimers
или глобально в конфигурации:
export default defineConfig({
test: {
fakeTimers: {
toFake: [
'setTimeout',
'clearTimeout',
'setInterval',
'clearInterval',
'setImmediate',
'clearImmediate',
'Date',
]
},
},
})
Ужесточение сравнения ошибок
Vitest теперь проверяет больше свойств при сравнении ошибок с помощью toEqual
или toThrowError
. Vitest теперь сравнивает name
, message
, cause
и AggregateError.errors
. Для Error.cause
сравнение выполняется асимметрично:
expect(new Error('hi', { cause: 'x' })).toEqual(new Error('hi')); // ✅
expect(new Error('hi')).toEqual(new Error('hi', { cause: 'x' })); // ❌
В дополнение к проверке большего количества свойств, Vitest теперь сравнивает прототипы ошибок. Например, если был выброшен TypeError
, сравнение должно ссылаться на TypeError
, а не на Error
:
expect(() => {
throw new TypeError('type error');
})
.toThrowError(new Error('type error'))
.toThrowError(new TypeError('type error'));
Подробнее см. в PR: #5876.
Условный экспорт module
не разрешается по умолчанию в Vite 6
Vite 6 позволяет использовать более гибкие опции resolve.conditions
, и Vitest настраивает его так, чтобы по умолчанию исключать условный экспорт module
. См. также руководство по миграции Vite 6 для получения подробной информации об изменениях на стороне Vite.
Тип Custom
объявлен устаревшим API
Тип Custom
теперь является синонимом типа Test
. Обратите внимание, что Vitest обновил публичные типы в 2.1 и изменил экспортируемые имена на RunnerCustomCase
и RunnerTestCase
:
import {
RunnerCustomCase,
RunnerTestCase,
} from 'vitest';
Если вы используете getCurrentSuite().custom()
, type
возвращаемой задачи теперь равен 'test'
. Тип Custom
будет удален в Vitest 4.
Тип WorkspaceSpec
не используется API
В публичном API этот тип ранее использовался в пользовательских секвенсорах. Пожалуйста, перейдите на TestSpecification
вместо него.
onTestFinished
и onTestFailed
теперь получают контекст
Хуки onTestFinished
и onTestFailed
ранее получали результат теста в качестве первого аргумента. Теперь они получают контекст теста, подобно beforeEach
и afterEach
.
Изменения в Snapshot API API
Публичный Snapshot API в @vitest/snapshot
был изменен для поддержки нескольких состояний за один запуск. Подробнее см. в PR: #6817
Обратите внимание, что эти изменения затрагивают только разработчиков, использующих Snapshot API напрямую. Изменений в API .toMatchSnapshot
не было.
Изменения в сигнатуре типа resolveConfig
API
Функция resolveConfig
теперь предоставляет больше возможностей. Вместо того чтобы принимать уже разрешенную конфигурацию Vite, она теперь принимает пользовательскую конфигурацию и возвращает разрешенную.
Эта функция не имеет внутреннего использования и предоставляется исключительно как публичный API.
Упорядочены типы vitest/reporters
API
Точка входа vitest/reporters
теперь экспортирует только реализации репортеров и типы параметров. Если вам нужен доступ к TestCase
/TestSuite
и другим типам, связанным с задачами, импортируйте их дополнительно из vitest/node
.
Покрытие игнорирует тестовые файлы, даже если coverage.excludes
переопределен.
Больше невозможно включать тестовые файлы в отчет о покрытии, переопределяя coverage.excludes
. Тестовые файлы теперь всегда исключаются.
Миграция на 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
удален
В связи с изменениями в пуле по умолчанию эта опция больше не нужна. Если вы столкнулись с ошибками сегментации памяти, попробуйте переключиться на пул '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 { vi } from 'vitest';
import type { Mock } 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
, если функция возвращала Promise. Теперь есть отдельное свойство 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 был разработан с Jest-совместимым API для максимально простой миграции с Jest. Несмотря на эти усилия, тем не менее, вы можете столкнуться со следующими различиями:
Глобальные переменные по умолчанию
Jest по умолчанию активирует свой глобальный API. Vitest не активирует его по умолчанию. Вы можете либо включить глобальные переменные через настройку конфигурации globals
, либо вместо этого обновить свой код для использования импортов из модуля vitest
.
Если вы решите оставить глобальные переменные отключенными, имейте в виду, что общие библиотеки, такие как testing-library
, не будут автоматически очищать DOM.
spy.mockReset
Метод mockReset
в Jest заменяет реализацию мока пустой функцией, возвращающей undefined
.
mockReset
в Vitest сбрасывает реализацию мока до его исходной. То есть, сброс мока, созданного vi.fn(impl)
, сбросит реализацию мока до impl
.
Моки модулей
При мокировании модуля в 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
), поэтому, если вы на него полагаетесь, не забудьте его переименовать. Vitest также предоставляет VITEST_WORKER_ID
, который является уникальным идентификатором запущенного воркера. Это число не зависит от maxThreads
и будет увеличиваться с каждым созданным воркером.
Замена свойства
Если вы хотите изменить объект, в 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()
})
}))
Хуки
В Vitest хуки beforeAll
/beforeEach
могут возвращать функцию очистки. По этой причине вам может потребоваться переписать объявления ваших хуков, если они возвращают что-либо, кроме 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);
В противном случае ваши снимки будут содержать много экранированных кавычек.