遷移指南
遷移至 Vitest 2.0
預設執行緒池變更為 forks
Vitest 2.0 已將 pool
的預設配置調整為 'forks'
,以提升穩定性。您可透過此 PR 深入了解其完整動機。
若您先前在使用 poolOptions
時未明確指定 pool
類型,則可能需要更新您的配置:
export default defineConfig({
test: {
poolOptions: {
threads: {
singleThread: true,
},
forks: {
singleFork: true,
},
},
},
});
鉤子(Hooks)以堆疊方式執行
在 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 { type Mock, vi } from 'vitest';
const add = (x: number, y: number): number => x + y;
// 使用 vi.fn<T>
const mockAdd = vi.fn<Parameters<typeof add>, ReturnType<typeof add>>();
const mockAdd = vi.fn<typeof add>();
// 使用 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 --typecheck
VITEST_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.startCurrentRun
client.resetCurrent
已重新命名為client.finishCurrentRun
Pool 標準化 #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"
}
}
Coverage 變更 #4265, #4442
選項 coverage.all
現在預設為啟用。這表示即使未執行,也會處理所有符合 coverage.include
樣式的專案檔案。
Coverage 臨界值 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,
+ }
}
}
})
Mock 類型變更 #4400
為了採用 Jest 風格的 "Mock" 命名,移除了一些類型。
- import { EnhancedSpy, SpyInstance } from 'vitest'
+ import { MockInstance } from 'vitest'
WARNING
SpyInstance
已棄用,建議使用 MockInstance
,並將在下一個主要版本中移除。
Timer Mock #3925
vi.useFakeTimers()
不再自動 mock process.nextTick
。 仍然可以透過使用 vi.useFakeTimers({ toFake: ['nextTick'] })
明確地指定來 mock process.nextTick
。
但是,使用 --pool=forks
時無法 mock process.nextTick
。如果您需要 process.nextTick
mocking,請使用不同的 --pool
選項。
從 Jest 遷移
Vitest 的設計與 Jest 的 API 相容,旨在使從 Jest 遷移盡可能簡單。儘管如此,您可能仍會遇到以下差異:
預設為全域變數
Jest 預設啟用其 全域 API。Vitest 則否。您可以透過 全域配置設定 啟用全域變數,或更新您的程式碼以改用從 vitest
模組匯入。
若您決定停用全域變數,請注意像 testing-library
這樣的常用函式庫將不會自動執行 DOM 清理。
模組模擬
在 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) => { // [!code --]
it('should work', () => new Promise(done => { // [!code ++]
// ...
done()
}) // [!code --]
})) // [!code ++]
鉤子(Hooks)
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);
否則您的快照將包含許多跳脫的 "
字元。