遷移指南
升級至 Vitest 3.0
測試選項作為第三個參數
如果您將物件作為第三個參數傳遞給 test 或 describe 函數,Vitest 3.0 會發出警告:
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 的情況下將 spy 重置為原始實作。現在,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 在監控物件時總是會分配一個新的 spy。這導致 mockRestore 出錯,因為它會將 spy 還原到先前的 spy,而不是原始函數:
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)。具體來說,當呼叫 vi.useFakeTimers 時,performance.now() 現在會被模擬。
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。
Vite 6 預設不解析 module 條件匯出
Vite 6 允許更靈活的 resolve.conditions 選項,Vitest 預設將其配置為排除 module 條件匯出。 有關 Vite 端變更的詳細資訊,請參閱 Vite 6 遷移指南。
Custom 類型已棄用 API
Custom 類型現在是 Test 類型的別名。請注意,Vitest 在 2.1 中更新了公開類型,並將匯出名稱更改為 RunnerCustomCase 和 RunnerTestCase:
import {
RunnerCustomCase,
RunnerTestCase,
} from 'vitest';如果您正在使用 getCurrentSuite().custom(),則返回任務的 type 現在等於 'test'。Custom 類型將在 Vitest 4 中移除。
WorkspaceSpec 類型不再使用 API
在公開 API 中,此類型以前用於自訂 sequencers。請改為遷移到 TestSpecification。
onTestFinished 和 onTestFailed 現在接收上下文
onTestFinished 和 onTestFailed 鉤子以前接收測試結果作為第一個參數。現在,它們接收測試上下文,就像 beforeEach 和 afterEach 一樣。
快照 API 的變更 API
@vitest/snapshot 中的公開快照 API 已更改,以支援單次執行中的多個狀態。有關更多詳細資訊,請參閱 PR:#6817
請注意,此變更僅影響直接使用快照 API 的開發人員。.toMatchSnapshot API 沒有任何變更。
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 中閱讀完整的動機。
如果您在未指定 pool 的情況下使用了 poolOptions,您可能需要更新配置:
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 限制)。
V8 覆蓋率的 coverage.ignoreEmptyLines 預設啟用
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 報告器
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
以前,如果函數返回 Promise,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 瀏覽器模式在 Beta 週期中發生了許多變更。您可以在 GitHub 討論頁面 上閱讀我們關於瀏覽器模式的理念。
大多數變更都是新增的,但也有一些小的破壞性變更:
已移除的棄用選項
一些棄用選項已移除:
vitest typecheck命令 - 請改用vitest --typecheckVITEST_JUNIT_CLASSNAME和VITEST_JUNIT_SUITE_NAME環境變數(請改用報告器選項)- 檢查
c8覆蓋率(請改用 coverage-v8) - 從
vitest匯出SnapshotEnvironment- 請改從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 套件也有 變更。如果您沒有直接使用它,則無需更改任何內容。
- 您不再需要僅為了覆寫
equalityCheck方法而擴展SnapshotClient:只需在實例化時將其作為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
為了採用 Jest 風格的「Mock」命名,一些類型已被移除。
- import { EnhancedSpy, SpyInstance } from 'vitest'
+ import { MockInstance } from 'vitest'WARNING
SpyInstance 已棄用,改用 MockInstance,並將在下一個主要版本中移除。
計時器模擬 #3925
vi.useFakeTimers() 不再自動模擬 process.nextTick。 仍然可以透過明確指定 vi.useFakeTimers({ toFake: ['nextTick'] }) 來模擬 process.nextTick。
但是,當使用 --pool=forks 時,無法模擬 process.nextTick。如果您需要 process.nextTick 模擬,請使用不同的 --pool 選項。
從 Jest 遷移
Vitest 的設計與 Jest 相容的 API,以便盡可能簡化從 Jest 的遷移。儘管如此,您仍然可能會遇到以下差異:
預設為全域變數
Jest 預設啟用其 全域變數 API。Vitest 則不然。您可以透過 全域變數配置設定 啟用全域變數,或更新您的程式碼以使用從 vitest 模組匯入的內容。
如果您決定停用全域變數,請注意像 testing-library 這樣的常用函式庫將不會自動執行 DOM 清理。
spy.mockReset
Jest 的 mockReset 會將模擬實作替換為一個返回 undefined 的空函數。
Vitest 的 mockReset 會將模擬實作重置為其原始實作。 也就是說,重置由 vi.fn(impl) 建立的模擬會將模擬實作重置為 impl。
模組模擬
在 Jest 中模擬模組時,工廠參數的返回值是預設匯出。在 Vitest 中,工廠參數必須返回一個明確定義每個匯出的物件。例如,以下 jest.mock 必須更新如下:
jest.mock('./some-path', () => 'hello')
vi.mock('./some-path', () => ({
default: 'hello',
})) 有關更多詳細資訊,請參閱 vi.mock API 部分。
自動模擬行為
與 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
Vitest 的 test 名稱以 > 符號連接,以便更容易區分測試和套件,而 Jest 使用空格 ()。
- `${describeTitle} ${testTitle}`
+ `${describeTitle} > ${testTitle}`環境變數
就像 Jest 一樣,如果 NODE_ENV 之前未設定,Vitest 會將其設定為 test。Vitest 也有一個 JEST_WORKER_ID 的對應項,稱為 VITEST_POOL_ID(總是小於或等於 maxThreads),因此如果您依賴它,請不要忘記重新命名它。Vitest 還公開了 VITEST_WORKER_ID,它是正在執行工作者的唯一 ID - 此數字不受 maxThreads 影響,並且會隨著每個建立的工作者而增加。
替換屬性
如果您想修改物件,您將在 Jest 中使用 replaceProperty API,您也可以在 Vitest 中使用 vi.stubEnv 或 vi.spyOn 來執行相同的操作。
完成回呼
從 Vitest v0.10.0 開始,宣告測試的回呼樣式已棄用。您可以將它們重寫為使用 async/await 函數,或使用 Promise 來模擬回呼樣式。
it('should work', (done) => {
it('should work', () => new Promise(done => {
// ...
done()
})
})) 鉤子
beforeAll/beforeEach 鉤子在 Vitest 中可能會返回 teardown 函數。因此,如果您的鉤子返回的不是 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);否則您的快照將會有許多逸出的 " 字元。