測試 API 參考文件
以下類型用於下面的型別簽章中
type Awaitable<T> = T | PromiseLike<T>;
type TestFunction = () => Awaitable<void>;
interface TestOptions {
/**
* 如果測試執行時間過長,測試將會失敗
*/
timeout?: number;
/**
* 如果測試失敗,將會重試特定次數
*
* @default 0
*/
retry?: number;
/**
* 即使每次都失敗,也會重複執行相同的測試數次
* 如果您有 "retry" 選項並且它失敗了,它將在每個循環週期中使用每次重試
* 對於調試隨機失敗非常有用
*
* @default 0
*/
repeats?: number;
}當測試函數返回一個 promise 時,執行器程式將等待它被解析以收集非同步期望值。如果 promise 被拒絕,測試將失敗。
TIP
在 Jest 中,TestFunction 也可以是 (done: DoneCallback) => void 類型。如果使用這種形式,測試將不會結束,直到 done 被調用。您可以使用 async 函數實現相同的效果,請參閱 遷移指南 Done Callback 章節。
test
類型:
(name: string | Function, fn: TestFunction, timeout?: number | TestOptions) => void別名:
ittest定義了一組相關的期望。它接收測試名稱和一個包含要測試的期望的函數。您可以選擇性地提供一個超時時間(以毫秒為單位),以指定在終止之前等待的時間。預設值為 5 秒,可以使用 testTimeout 進行全域設定。
tsimport { expect, test } from 'vitest'; test('should work as expected', () => { expect(Math.sqrt(4)).toBe(2); });
test.extend
類型:
<T extends Record<string, any>>(fixtures: Fixtures<T>): TestAPI<ExtraContext & T>別名:
it.extend版本: Vitest 0.32.3
使用
test.extend來擴展測試上下文,使其具有自定義測試固定裝置(fixtures)。這將返回一個新的test並且也是可擴展的,因此您可以根據需要通過擴展它來組合更多 fixtures 或覆蓋現有的 fixtures。有關更多資訊,請參閱 擴展測試上下文。tsimport { expect, test } from 'vitest'; const todos = []; const archive = []; const myTest = test.extend({ todos: async ({ task }, use) => { todos.push(1, 2, 3); await use(todos); todos.length = 0; }, archive, }); myTest('add item', ({ todos }) => { expect(todos.length).toBe(3); todos.push(4); expect(todos.length).toBe(4); });
test.skip
類型:
(name: string | Function, fn: TestFunction, timeout?: number | TestOptions) => void別名:
it.skip如果您想跳過運行某些測試,但基於任何原因不想刪除代碼,您可以使用
test.skip來避免運行它們。tsimport { assert, test } from 'vitest'; test.skip('skipped test', () => { // Test skipped, no error assert.equal(Math.sqrt(4), 3); });您還可以通過在其 context 上動態調用
skip來跳過測試:tsimport { assert, test } from 'vitest'; test('skipped test', context => { context.skip(); // Test skipped, no error assert.equal(Math.sqrt(4), 3); });
test.skipIf
類型:
(condition: any) => Test別名:
it.skipIf在某些情況下,您可能會使用不同的環境多次運行測試,並且某些測試可能是特定於環境的。您可以不用
if包裹測試代碼,改為使用test.skipIf在條件為真時跳過測試。tsimport { assert, test } from 'vitest'; const isDev = process.env.NODE_ENV === 'development'; test.skipIf(isDev)('prod only test', () => { // this test only runs in production });
WARNING
當使用 Vitest 作為 類型檢查器 時,您不能使用此語法。
test.runIf
類型:
(condition: any) => Test別名:
it.runIf與 test.skipIf 相反。
tsimport { assert, test } from 'vitest'; const isDev = process.env.NODE_ENV === 'development'; test.runIf(isDev)('dev only test', () => { // this test only runs in development });
WARNING
當使用 Vitest 作為 類型檢查器 時,您不能使用此語法。
test.only
類型:
(name: string | Function, fn: TestFunction, timeout?: number) => void別名:
it.only使用
test.only僅運行給定套件中的某些測試。這在調試時很有用。您可以選擇性地提供一個超時時間(以毫秒為單位),以指定在終止之前等待的時間。預設值為 5 秒,可以使用 testTimeout 進行全域設定。
tsimport { assert, test } from 'vitest'; test.only('test', () => { // Only this test (and others marked with only) are run assert.equal(Math.sqrt(4), 2); });有時,僅運行特定文件中的
only測試非常有用,藉此忽略來自整個測試套件的所有其他測試,避免這些測試影響輸出結果。為了做到這一點,請使用包含相關測試的特定文件運行
vitest。# vitest interesting.test.ts
test.concurrent
類型:
(name: string | Function, fn: TestFunction, timeout?: number) => void別名:
it.concurrenttest.concurrent標記連續的測試以並行運行。它接收測試名稱、一個包含要收集的測試的異步函數和一個可選的超時時間(以毫秒為單位)。tsimport { describe, test } from 'vitest'; // The two tests marked with concurrent will be run in parallel describe('suite', () => { test('serial test', async () => { /* ... */ }); test.concurrent('concurrent test 1', async () => { /* ... */ }); test.concurrent('concurrent test 2', async () => { /* ... */ }); });test.skip、test.only和test.todo適用於並行測試。以下所有組合都是有效的:tstest.concurrent(/* ... */); test.skip.concurrent(/* ... */); // or test.concurrent.skip(/* ... */) test.only.concurrent(/* ... */); // or test.concurrent.only(/* ... */) test.todo.concurrent(/* ... */); // or test.concurrent.todo(/* ... */)運行並行測試時,快照檔和斷言檢查必須使用來自本地 測試上下文 的
expect,以確保能檢測到正確的測試。tstest.concurrent('test 1', async ({ expect }) => { expect(foo).toMatchSnapshot(); }); test.concurrent('test 2', async ({ expect }) => { expect(foo).toMatchSnapshot(); });
WARNING
當使用 Vitest 作為 類型檢查器 時,您不能使用此語法。
test.todo
類型:
(name: string | Function) => void別名:
it.todo使用
test.todo來建立稍後要實現的測試樁。報告中將顯示測試的項目,以便您知道還需要實現多少測試。ts// An entry will be shown in the report for this test test.todo('unimplemented test');
test.fails
類型:
(name: string | Function, fn: TestFunction, timeout?: number) => void別名:
it.fails使用
test.fails來指示斷言檢查將顯式失敗。tsimport { expect, test } from 'vitest'; function myAsyncFunc() { return new Promise(resolve => resolve(1)); } test.fails('fail test', async () => { await expect(myAsyncFunc()).rejects.toBe(1); });
WARNING
當使用 Vitest 作為 類型檢查器 時,您不能使用此語法。
test.each
類型:
(cases: ReadonlyArray<T>, ...args: any[]) => void別名:
it.each當您需要使用不同的變數運行相同的測試時,請使用
test.each。 您可以使用 printf 格式化,依照測試函數參數的順序,在測試名稱中注入參數。%s: 字串%d: 數字%i: 整數%f: 浮點數值%j: json%o: 物件%#: 測試案例的索引%%: 單個百分號 ('%')
tstest.each([ [1, 1, 2], [1, 2, 3], [2, 1, 3], ])('add(%i, %i) -> %i', (a, b, expected) => { expect(a + b).toBe(expected); }); // 這將返回 // ✓ add(1, 1) -> 2 // ✓ add(1, 2) -> 3 // ✓ add(2, 1) -> 3如果您使用物件作為參數,您也可以使用
$前綴訪問物件屬性:tstest.each([ { a: 1, b: 1, expected: 2 }, { a: 1, b: 2, expected: 3 }, { a: 2, b: 1, expected: 3 }, ])('add($a, $b) -> $expected', ({ a, b, expected }) => { expect(a + b).toBe(expected); }); // 這將返回 // ✓ add(1, 1) -> 2 // ✓ add(1, 2) -> 3 // ✓ add(2, 1) -> 3如果您使用物件作為參數,您也可以使用
.訪問物件屬性:tstest.each` a | b | expected ${{ val: 1 }} | ${'b'} | ${'1b'} ${{ val: 2 }} | ${'b'} | ${'2b'} ${{ val: 3 }} | ${'b'} | ${'3b'} `('add($a.val, $b) -> $expected', ({ a, b, expected }) => { expect(a.val + b).toBe(expected); }); // 這將返回 // ✓ add(1, b) -> 1b // ✓ add(2, b) -> 2b // ✓ add(3, b) -> 3b從 Vitest 0.25.3 開始,您也可以使用模板字串表。
- 第一行應該是欄位名稱,用
|分隔; - 一個或多個後續的資料列作為使用
${value}語法的模板文字表達式提供。
tstest.each` a | b | expected ${1} | ${1} | ${2} ${'a'} | ${'b'} | ${'ab'} ${[]} | ${'b'} | ${'b'} ${{}} | ${'b'} | ${'[object Object]b'} ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('returns $expected when $a is added $b', ({ a, b, expected }) => { expect(a + b).toBe(expected); });如果您想訪問
TestContext,請將describe.each與單個測試一起使用。
TIP
Vitest 使用 chai format 方法處理 $values。如果值被截斷得太厲害,您可以在配置文件中增加 chaiConfig.truncateThreshold。
WARNING
當使用 Vitest 作為 類型檢查器 時,您不能使用此語法。
bench
- 類型:
(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
bench 定義一個基準測試程式(benchmark)。在 Vitest 的術語中,基準測試是一個定義一系列操作的函數。Vitest 多次運行此函數以顯示不同的性能結果。
Vitest 在底層使用 tinybench 庫,繼承了它的所有選項,這些選項可以用作第三個參數。
import { bench } from 'vitest';
bench(
'normal sorting',
() => {
const x = [1, 5, 4, 2, 3];
x.sort((a, b) => {
return a - b;
});
},
{ time: 1000 }
);export interface Options {
/**
* 運行基準測試任務所需的時間(毫秒)
* @default 500
*/
time?: number;
/**
* 即使 time 選項已完成,任務也應運行的次數
* @default 10
*/
iterations?: number;
/**
* 獲取當前時間戳(以毫秒為單位)的函數
*/
now?: () => number;
/**
* 用於中止基準測試的 AbortSignal
*/
signal?: AbortSignal;
/**
* 暖機時間(毫秒)
* @default 100ms
*/
warmupTime?: number;
/**
* 暖機迭代次數
* @default 5
*/
warmupIterations?: number;
/**
* 在每個基準測試任務(循環週期)之前運行的設置函數
*/
setup?: Hook;
/**
* 在每個基準測試任務(循環週期)之後運行的拆卸函式
*/
teardown?: Hook;
}bench.skip
- 類型:
(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
您可以使用 bench.skip 語法來跳過運行某些基準測試。
import { bench } from 'vitest';
bench.skip('normal sorting', () => {
const x = [1, 5, 4, 2, 3];
x.sort((a, b) => {
return a - b;
});
});bench.only
- 類型:
(name: string | Function, fn: BenchFunction, options?: BenchOptions) => void
使用 bench.only 僅運行給定套件中的某些基準測試。這在調試時很有用。
import { bench } from 'vitest';
bench.only('normal sorting', () => {
const x = [1, 5, 4, 2, 3];
x.sort((a, b) => {
return a - b;
});
});bench.todo
- 類型:
(name: string | Function) => void
使用 bench.todo 來建立稍後要實現的基準測試樁。
import { bench } from 'vitest';
bench.todo('unimplemented test');describe
當你在檔案的最上層使用 test 或 bench 時,它們會被收集為該檔案隱含的測試套件(suite)的一部分。使用 describe 你可以在目前的上下文中定義一個新的測試套件,作為一組相關的測試或基準測試,以及其他巢狀的測試套件。一個測試套件可以讓你組織你的測試和基準測試,使報告更加清晰。
// basic.spec.ts
// 組織測試
import { describe, expect, test } from 'vitest';
const person = {
isActive: true,
age: 32,
};
describe('person', () => {
test('person is defined', () => {
expect(person).toBeDefined();
});
test('is active', () => {
expect(person.isActive).toBeTruthy();
});
test('age limit', () => {
expect(person.age).toBeLessThanOrEqual(32);
});
});// basic.bench.ts
// 組織基準測試
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;
});
});
});如果你有一個測試或基準測試的階層結構,你也可以巢狀 describe 測試套件:
import { describe, expect, test } from 'vitest';
function numberToCurrency(value) {
if (typeof value !== 'number') throw new Error('Value must be a number');
return value
.toFixed(2)
.toString()
.replace(/\B(?=(\d{3})+(?!\d))/g, ',');
}
describe('numberToCurrency', () => {
describe('given an invalid number', () => {
test('composed of non-numbers to throw error', () => {
expect(() => numberToCurrency('abc')).toThrowError();
});
});
describe('given a valid number', () => {
test('returns the correct currency format', () => {
expect(numberToCurrency(10000)).toBe('10,000.00');
});
});
});describe.skip
類型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void在一個測試套件中使用
describe.skip來避免執行特定的describe測試套件。tsimport { assert, describe, test } from 'vitest'; describe.skip('skipped suite', () => { test('sqrt', () => { // 測試套件已略過,沒有錯誤 assert.equal(Math.sqrt(4), 3); }); });
describe.skipIf
類型:
(condition: any) => void在某些情況下,你可能會在不同環境中多次運行測試套件,並且某些測試套件可能是環境專屬的。你可以使用
describe.skipIf來跳過測試套件,只要條件為真值,而不需要用if包裹測試套件。tsimport { describe, test } from 'vitest'; const isDev = process.env.NODE_ENV === 'development'; describe.skipIf(isDev)('prod only test', () => { // 此測試僅在生產環境中執行 });
WARNING
當使用 Vitest 作為類型檢查器時,你不能使用此語法。
describe.only
類型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => void使用
describe.only僅運行特定的測試套件。ts// 僅執行此測試套件(以及其他標記為 `only` 的測試套件) describe.only('suite', () => { test('sqrt', () => { assert.equal(Math.sqrt(4), 3); }); }); describe('other suite', () => { // ... 將被跳過 });有時,在特定檔案中運行
only測試非常有用,忽略整個測試套件中的所有其他測試,這些測試會影響輸出結果。為了做到這一點,請使用包含相關測試的特定檔案執行
vitest。# vitest interesting.test.ts
describe.concurrent
類型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => voiddescribe.concurrent會將測試套件中的每個測試標記為並行(concurrent)。ts// 此測試套件中的所有測試都將並行執行 describe.concurrent('suite', () => { test('concurrent test 1', async () => { /* ... */ }); test('concurrent test 2', async () => { /* ... */ }); test.concurrent('concurrent test 3', async () => { /* ... */ }); });.skip、.only和.todo適用於並行測試套件。以下所有組合都是有效的:tsdescribe.concurrent(/* ... */); describe.skip.concurrent(/* ... */); // or describe.concurrent.skip(/* ... */) describe.only.concurrent(/* ... */); // or describe.concurrent.only(/* ... */) describe.todo.concurrent(/* ... */); // or describe.concurrent.todo(/* ... */)
當執行並行測試時,快照(Snapshots)和斷言(Assertions)必須使用來自本地測試環境的 expect,以確保檢測到正確的測試。
describe.concurrent('suite', () => {
test('concurrent test 1', async ({ expect }) => {
expect(foo).toMatchSnapshot();
});
test('concurrent test 2', async ({ expect }) => {
expect(foo).toMatchSnapshot();
});
});WARNING
當使用 Vitest 作為類型檢查器時,你不能使用此語法。
describe.sequential
類型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => voiddescribe.sequential會將測試套件中的每個測試標記為循序(sequential)。如果你想在describe.concurrent中或使用--sequence.concurrent命令選項循序執行測試,這非常有用。tsdescribe.concurrent('suite', () => { test('concurrent test 1', async () => { /* ... */ }); test('concurrent test 2', async () => { /* ... */ }); describe.sequential('', () => { test('sequential test 1', async () => { /* ... */ }); test('sequential test 2', async () => { /* ... */ }); }); });
describe.shuffle
類型:
(name: string | Function, fn: TestFunction, options?: number | TestOptions) => voidVitest 提供了一種通過 CLI 標誌
--sequence.shuffle或配置選項sequence.shuffle以隨機順序執行所有測試的方法,但如果你只想讓部分測試套件以隨機順序執行,可以使用此標記。tsdescribe.shuffle('suite', () => { test('random test 1', async () => { /* ... */ }); test('random test 2', async () => { /* ... */ }); test('random test 3', async () => { /* ... */ }); }); // 順序取決於配置中的 sequence.seed 選項(預設為 Date.now())
.skip、.only 和 .todo 適用於隨機測試套件。
WARNING
當使用 Vitest 作為類型檢查器時,你不能使用此語法。
describe.todo
類型:
(name: string | Function) => void使用
describe.todo來預留稍後要實作的測試套件。報告中會顯示測試項目,方便你了解尚有多少測試需要實作。ts// 將在報告中顯示此測試套件的條目 describe.todo('unimplemented suite');
describe.each
類型:
(cases: ReadonlyArray<T>, ...args: any[]): (name: string | Function, fn: (...args: T[]) => void, options?: number | TestOptions) => void如果你有多個測試依賴於相同的數據,請使用
describe.each。tsdescribe.each([ { a: 1, b: 1, expected: 2 }, { a: 1, b: 2, expected: 3 }, { a: 2, b: 1, expected: 3 }, ])('describe object add($a, $b)', ({ a, b, expected }) => { test(`returns ${expected}`, () => { expect(a + b).toBe(expected); }); test(`returned value not be greater than ${expected}`, () => { expect(a + b).not.toBeGreaterThan(expected); }); test(`returned value not be less than ${expected}`, () => { expect(a + b).not.toBeLessThan(expected); }); });從 Vitest 0.25.3 開始,你也可以使用模板字串表格。
- 第一行應該是列名,用
|分隔; - 一個或多個後續的數據行,使用
${value}語法作為模板字面量表達式提供。
tsdescribe.each` a | b | expected ${1} | ${1} | ${2} ${'a'} | ${'b'} | ${'ab'} ${[]} | ${'b'} | ${'b'} ${{}} | ${'b'} | ${'[object Object]b'} ${{ asd: 1 }} | ${'b'} | ${'[object Object]b'} `('describe template string add($a, $b)', ({ a, b, expected }) => { test(`returns ${expected}`, () => { expect(a + b).toBe(expected); }); });- 第一行應該是列名,用
WARNING
當使用 Vitest 作為類型檢查器時,你不能使用此語法。
設定與拆卸
這些函式允許你鉤入測試的生命週期,以避免重複設定和拆卸程式碼。它們適用於目前的上下文:如果在最上層使用它們,則適用於檔案;如果在 describe 測試套件內使用它們,則適用於目前的測試套件。當你將 Vitest 作為類型檢查器執行時,不會呼叫這些鉤點。
beforeEach
類型:
beforeEach(fn: () => Awaitable<void>, timeout?: number)註冊一個回呼函式,此函式會在目前上下文中每個測試執行前被呼叫。 如果該函式返回一個 Promise,Vitest 會等待 Promise 解析後再執行測試。
你可以選擇性地傳遞逾時時間 (毫秒),設定終止前的等待時間。預設值為 5 秒。
tsimport { beforeEach } from 'vitest'; beforeEach(async () => { // 在每次測試執行前清除模擬資料並新增一些測試資料 await stopMocking(); await addUser({ name: 'John' }); });在這裡,
beforeEach確保為每個測試添加使用者。自 Vitest v0.10.0 起,
beforeEach也接受一個可選的清除函式(相當於afterEach)。tsimport { beforeEach } from 'vitest'; beforeEach(async () => { // 在每次測試執行之前呼叫一次 await prepareSomething(); // 清除函式,在每次測試執行之後呼叫一次 return async () => { await resetSomething(); }; });
afterEach
類型:
afterEach(fn: () => Awaitable<void>, timeout?: number)註冊一個回呼函式,此函式會在目前上下文中每個測試完成後被呼叫。 如果該函式返回一個 Promise,Vitest 會等待 Promise 解析後再繼續。
你可以選擇性地提供逾時時間 (毫秒),用於指定終止前的等待時間。預設值為 5 秒。
tsimport { afterEach } from 'vitest'; afterEach(async () => { await clearTestingData(); // 在每次測試執行後清除測試資料 });在這裡,
afterEach確保在每次測試執行後清除測試資料。
beforeAll
類型:
beforeAll(fn: () => Awaitable<void>, timeout?: number)註冊一個回呼函式,此函式會在開始執行目前上下文中所有測試前被呼叫一次。 如果該函式返回一個 Promise,Vitest 會等待 Promise 解析後再執行測試。
你可以選擇性地提供逾時時間 (毫秒),用於指定終止前的等待時間。預設值為 5 秒。
tsimport { beforeAll } from 'vitest'; beforeAll(async () => { await startMocking(); // 在所有測試執行之前呼叫一次 });在此,
beforeAll確保在測試執行前設定模擬資料。自 Vitest v0.10.0 起,
beforeAll也接受一個可選的清除函式(相當於afterAll)。tsimport { beforeAll } from 'vitest'; beforeAll(async () => { // 在所有測試執行之前呼叫一次 await startMocking(); // 清除函式,在所有測試執行之後呼叫一次 return async () => { await stopMocking(); }; });
afterAll
類型:
afterAll(fn: () => Awaitable<void>, timeout?: number)註冊一個回呼函式,此函式會在目前上下文中所有測試執行完畢後被呼叫一次。 如果該函式返回一個 Promise,Vitest 會等待 Promise 解析後再繼續。
你可以選擇性地提供逾時時間 (毫秒),用於指定終止前的等待時間。預設值為 5 秒。
tsimport { afterAll } from 'vitest'; afterAll(async () => { await stopMocking(); // 此方法在所有測試執行完畢後呼叫 });在此,
afterAll確保在所有測試執行完畢後呼叫stopMocking函式。