Руководство по миграции
Миграция на 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.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 был разработан с 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);В противном случае ваши снимки будут содержать много экранированных кавычек.