功能特色
- Vite 的配置、轉換器、解析器與外掛支援
- 測試執行時,沿用應用程式的既有設定
- 智慧即時監控模式,提供類似 HMR 的測試體驗
- 支援 Vue、React、Svelte、Lit、Marko 等框架的元件測試
- 開箱即用的 TypeScript / JSX 支援
- ESM 優先,支援頂層 await
- 透過 Tinypool 實現 Worker 多執行緒
- 透過 Tinybench 支援基準測試
- 支援測試套件與測試的篩選、逾時設定及並行執行
- 專案支援
- Jest 相容快照
- 內建 Chai 用於斷言,並提供 Jest expect 相容 API
- 內建 Tinyspy 支援模擬功能
- happy-dom 或 jsdom 支援 DOM 模擬
- 瀏覽器模式,用於在瀏覽器環境中執行元件測試
- 透過 v8 或 istanbul 進行程式碼覆蓋率分析
- 類似 Rust 的原始碼內測試
- 透過 expect-type 進行型別測試
- 分片支援
- 報告未捕獲的錯誤
測試、開發和建置之間的共用配置
Vitest 沿用 Vite 的配置、轉換器、解析器與外掛,確保測試執行時能使用與您的應用程式相同的設定。
在配置 Vitest中了解更多。
監控模式
$ vitest
當您修改原始碼或測試檔案時,Vitest 會智慧地搜尋模組圖並僅重新執行相關測試,如同 Vite 中的 HMR (Hot Module Replacement) 一般!
在開發環境中,vitest
預設以 watch mode
啟動;而在 CI 環境中(當 process.env.CI
存在時),則會智慧地以 run mode
啟動。您可以使用 vitest watch
或 vitest run
來明確指定所需的模式。
使用 --standalone
旗標啟動 Vitest,使其在背景執行。它不會執行任何測試,除非相關檔案發生變更。即使原始碼發生變更,Vitest 也只會在匯入該原始碼的測試執行後,才會重新執行相關測試。
開箱即用的常見 Web 慣例
開箱即用支援 ES 模組、TypeScript、JSX 和 PostCSS。
執行緒
預設情況下,Vitest 會使用 node:child_process
透過 Tinypool(Piscina 的輕量級分支)在多個程序中執行測試檔案,實現測試的同時執行。如果您想進一步加快測試套件的速度,請考慮啟用 --pool=threads
以使用 node:worker_threads
執行測試(請留意,某些套件可能不適用於此設定)。
要在單一執行緒或程序中執行測試,請參閱 poolOptions
。
Vitest 還會隔離每個檔案的環境,確保一個檔案中的環境變更不會影響其他檔案。您可以透過向 CLI 傳遞 --no-isolate
來禁用隔離(這會犧牲正確性以換取執行效能)。
測試篩選
Vitest 提供了多種方法來縮小測試執行範圍,以加快測試速度,讓您可以專注於開發。
在測試篩選中了解更多。
並行執行測試
在連續測試中使用 .concurrent
,使其並行執行。
import { describe, it } from 'vitest';
// 兩個標記為 concurrent 的測試將並行執行
describe('suite', () => {
it('serial test', async () => {
/* ... */
});
it.concurrent('concurrent test 1', async ({ expect }) => {
/* ... */
});
it.concurrent('concurrent test 2', async ({ expect }) => {
/* ... */
});
});
如果您在測試套件上使用 .concurrent
,則其中的每個測試都將並行執行。
import { describe, it } from 'vitest';
// 此測試套件中的所有測試都將並行執行
describe.concurrent('suite', () => {
it('concurrent test 1', async ({ expect }) => {
/* ... */
});
it('concurrent test 2', async ({ expect }) => {
/* ... */
});
it.concurrent('concurrent test 3', async ({ expect }) => {
/* ... */
});
});
您還可以將 .skip
、.only
和 .todo
與並行套件和測試一起使用。在API 參考中閱讀更多內容。
WARNING
執行並行測試時,快照和斷言必須使用來自本地測試上下文的 expect
,以確保正確識別測試。
快照
Jest 相容的快照支援。
import { expect, it } from 'vitest';
it('renders correctly', () => {
const result = render();
expect(result).toMatchSnapshot();
});
在快照中了解更多。
Chai 和 Jest expect
相容性
Chai 內建用於斷言,並提供 Jest expect
相容 API。
請留意,如果您使用會新增匹配器的第三方函式庫,將 test.globals
設定為 true
將提供更好的相容性。
模擬
Tinyspy 內建用於模擬,並在 vi
物件上提供 jest
相容 API。
import { expect, vi } from 'vitest';
const fn = vi.fn();
fn('hello', 1);
expect(vi.isMockFunction(fn)).toBe(true);
expect(fn.mock.calls[0]).toEqual(['hello', 1]);
fn.mockImplementation((arg: string) => arg);
fn('world', 2);
expect(fn.mock.results[1].value).toBe('world');
Vitest 支援 happy-dom 或 jsdom 來模擬 DOM 和瀏覽器 API。它們不隨 Vitest 一起提供,您需要單獨安裝:
$ npm i -D happy-dom
$ npm i -D jsdom
之後,在您的配置檔案中更改 environment
選項:
import { defineConfig } from 'vitest/config';
export default defineConfig({
test: {
environment: 'happy-dom', // 或 'jsdom', 'node'
},
});
在模擬中了解更多。
覆蓋率
Vitest 支援透過 v8
進行原生程式碼覆蓋率分析,以及透過 istanbul
進行儀器化程式碼覆蓋率分析。
{
"scripts": {
"test": "vitest",
"coverage": "vitest run --coverage"
}
}
在覆蓋率中了解更多。
原始碼內測試
Vitest 還提供了一種在原始碼中執行測試的方法,與實作並存,類似於 Rust 的模組測試。
這使得測試與實作共享相同的閉包,並能在不匯出的情況下測試私有狀態。同時,這也讓開發的回饋循環更為緊密。
// 實作
export function add(...args: number[]): number {
return args.reduce((a, b) => a + b, 0);
}
// 原始碼內測試套件
if (import.meta.vitest) {
const { it, expect } = import.meta.vitest;
it('add', () => {
expect(add()).toBe(0);
expect(add(1)).toBe(1);
expect(add(1, 2, 3)).toBe(6);
});
}
在原始碼內測試中了解更多。
基準測試 實驗性
您可以使用 bench
函式透過 Tinybench 執行基準測試,以比較效能。
import { bench, describe } from 'vitest';
describe('sort', () => {
bench('normal', () => {
const x = [1, 5, 4, 2, 3];
x.sort((a, b) => {
return a - b;
});
});
bench('reverse', () => {
const x = [1, 5, 4, 2, 3];
x.reverse().sort((a, b) => {
return a - b;
});
});
});
型別測試 實驗性
您可以編寫測試來偵測型別迴歸。Vitest 隨附 expect-type
套件,提供類似且易於理解的 API。
import { assertType, expectTypeOf, test } from 'vitest';
import { mount } from './mount.js';
test('my types work properly', () => {
expectTypeOf(mount).toBeFunction();
expectTypeOf(mount).parameter(0).toMatchTypeOf<{ name: string }>();
// @ts-expect-error name is a string
assertType(mount({ name: 42 }));
});
分片
使用 --shard
和 --reporter=blob
參數在不同的機器上執行測試。 所有測試和覆蓋率結果都可以在 CI 管道結束時使用 --merge-reports
指令合併:
vitest --shard=1/2 --reporter=blob --coverage
vitest --shard=2/2 --reporter=blob --coverage
vitest --merge-reports --reporter=junit --coverage
有關更多資訊,請參閱 提升效能 | 分片
。
環境變數
Vitest 會自動載入 .env
檔案中以 VITE_
為字首的環境變數,以保持與前端相關測試的相容性,並遵循 Vite 既定的慣例。若要從 .env
檔案載入所有環境變數,您可以使用從 vite
匯入的 loadEnv
方法:
import { loadEnv } from 'vite';
import { defineConfig } from 'vitest/config';
export default defineConfig(({ mode }) => ({
test: {
// mode 定義了在存在時應選擇哪個 ".env.{mode}" 檔案。
env: loadEnv(mode, process.cwd(), ''),
},
}));
未處理的錯誤
預設情況下,Vitest 會捕獲並報告所有未處理的 Promise 拒絕、未捕獲的例外(在 Node.js 中)和 錯誤 事件(在瀏覽器中)。
您可以透過手動捕獲這些錯誤來禁用此行為。Vitest 會假定該回呼已由您處理,因此不會報告錯誤。
// 在 Node.js 中
process.on('unhandledRejection', () => {
// 您自己的處理器
});
process.on('uncaughtException', () => {
// 您自己的處理器
});
// 在瀏覽器中
window.addEventListener('error', () => {
// 您自己的處理器
});
window.addEventListener('unhandledrejection', () => {
// 您自己的處理器
});
或者,您也可以使用 dangerouslyIgnoreUnhandledErrors
選項來忽略已報告的錯誤。Vitest 仍會報告這些錯誤,但它們不會影響測試結果(結束代碼將保持不變)。
如果您需要測試錯誤是否未被捕獲,您可以建立一個如下所示的測試:
test('my function throws uncaught error', async ({ onTestFinished }) => {
onTestFinished(() => {
// 如果在測試期間從未觸發此事件,
// 請確保在下一個測試開始前將其移除
process.removeAllListeners('unhandledrejection');
});
return new Promise((resolve, reject) => {
process.once('unhandledrejection', error => {
try {
expect(error.message).toBe('my error');
resolve();
} catch (error) {
reject(error);
}
});
callMyFunctionThatRejectsError();
});
});