跳到主要內容
版本:29.7

設定和結束

在撰寫測試時,通常會有一些設定工作需要在測試執行前進行,還有一些結束工作需要在測試執行後進行。Jest 提供了輔助函式來處理這項工作。

重複設定

如果您有一些工作需要重複執行多次測試,可以使用 beforeEachafterEach 鉤子。

例如,假設有幾個測試與城市資料庫進行互動。您有一個方法 initializeCityDatabase(),必須在這些測試的每個測試之前呼叫,還有一個方法 clearCityDatabase(),必須在這些測試的每個測試之後呼叫。您可以使用下列方式執行此操作

beforeEach(() => {
initializeCityDatabase();
});

afterEach(() => {
clearCityDatabase();
});

test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});

beforeEachafterEach 可以處理非同步程式碼,其方式與 測試處理非同步程式碼 的方式相同,它們可以採用 done 參數或傳回承諾。例如,如果 initializeCityDatabase() 傳回一個承諾,在資料庫初始化時解析,我們會希望傳回該承諾

beforeEach(() => {
return initializeCityDatabase();
});

一次性設定

在某些情況下,您只需要在檔案開頭進行一次設定。當設定是非同步時,這可能會特別麻煩,因此您無法內嵌執行。Jest 提供了 beforeAllafterAll 鉤子來處理這種情況。

例如,如果 initializeCityDatabase()clearCityDatabase() 都傳回承諾,且城市資料庫可以在測試之間重複使用,我們可以將測試程式碼變更為

beforeAll(() => {
return initializeCityDatabase();
});

afterAll(() => {
return clearCityDatabase();
});

test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});

範圍

頂層的 before*after* 掛鉤套用於檔案中的每個測試。在 describe 區塊內宣告的掛鉤只套用於該 describe 區塊內的測試。

例如,假設我們不僅有城市資料庫,還有食物資料庫。我們可以為不同的測試執行不同的設定

// Applies to all tests in this file
beforeEach(() => {
return initializeCityDatabase();
});

test('city database has Vienna', () => {
expect(isCity('Vienna')).toBeTruthy();
});

test('city database has San Juan', () => {
expect(isCity('San Juan')).toBeTruthy();
});

describe('matching cities to foods', () => {
// Applies only to tests in this describe block
beforeEach(() => {
return initializeFoodDatabase();
});

test('Vienna <3 veal', () => {
expect(isValidCityFoodPair('Vienna', 'Wiener Schnitzel')).toBe(true);
});

test('San Juan <3 plantains', () => {
expect(isValidCityFoodPair('San Juan', 'Mofongo')).toBe(true);
});
});

請注意,頂層的 beforeEach 會在 describe 區塊內的 beforeEach 之前執行。這有助於說明所有掛鉤的執行順序。

beforeAll(() => console.log('1 - beforeAll'));
afterAll(() => console.log('1 - afterAll'));
beforeEach(() => console.log('1 - beforeEach'));
afterEach(() => console.log('1 - afterEach'));

test('', () => console.log('1 - test'));

describe('Scoped / Nested block', () => {
beforeAll(() => console.log('2 - beforeAll'));
afterAll(() => console.log('2 - afterAll'));
beforeEach(() => console.log('2 - beforeEach'));
afterEach(() => console.log('2 - afterEach'));

test('', () => console.log('2 - test'));
});

// 1 - beforeAll
// 1 - beforeEach
// 1 - test
// 1 - afterEach
// 2 - beforeAll
// 1 - beforeEach
// 2 - beforeEach
// 2 - test
// 2 - afterEach
// 1 - afterEach
// 2 - afterAll
// 1 - afterAll

執行順序

Jest 會在執行任何實際測試之前執行測試檔案中的所有 describe 處理常式。這是另一個原因,說明在 before*after* 處理常式內而非 describe 區塊內執行設定和清除。一旦 describe 區塊完成,預設情況下,Jest 會按收集階段遇到的順序依序執行所有測試,並等到每個測試完成並整理完畢後才繼續進行。

考慮以下說明性的測試檔案和輸出

describe('describe outer', () => {
console.log('describe outer-a');

describe('describe inner 1', () => {
console.log('describe inner 1');

test('test 1', () => console.log('test 1'));
});

console.log('describe outer-b');

test('test 2', () => console.log('test 2'));

describe('describe inner 2', () => {
console.log('describe inner 2');

test('test 3', () => console.log('test 3'));
});

console.log('describe outer-c');
});

// describe outer-a
// describe inner 1
// describe outer-b
// describe inner 2
// describe outer-c
// test 1
// test 2
// test 3

就像 describetest 區塊一樣,Jest 會按宣告順序呼叫 before*after* 掛鉤。請注意,會先呼叫封閉範圍的 after* 掛鉤。例如,以下是如何設定和清除彼此依賴的資源

beforeEach(() => console.log('connection setup'));
beforeEach(() => console.log('database setup'));

afterEach(() => console.log('database teardown'));
afterEach(() => console.log('connection teardown'));

test('test 1', () => console.log('test 1'));

describe('extra', () => {
beforeEach(() => console.log('extra database setup'));
afterEach(() => console.log('extra database teardown'));

test('test 2', () => console.log('test 2'));
});

// connection setup
// database setup
// test 1
// database teardown
// connection teardown

// connection setup
// database setup
// extra database setup
// test 2
// extra database teardown
// database teardown
// connection teardown
備註

如果您使用 jasmine2 測試執行器,請考慮它會以相反的宣告順序呼叫 after* 掛鉤。若要產生相同的輸出,上述範例應修改如下

  beforeEach(() => console.log('connection setup'));
+ afterEach(() => console.log('connection teardown'));

beforeEach(() => console.log('database setup'));
+ afterEach(() => console.log('database teardown'));

- afterEach(() => console.log('database teardown'));
- afterEach(() => console.log('connection teardown'));

// ...

一般建議

如果測試失敗,首先要檢查的事情之一是,當測試是唯一執行的測試時,測試是否會失敗。若要使用 Jest 僅執行一個測試,請暫時將 test 指令變更為 test.only

test.only('this will be the only test that runs', () => {
expect(true).toBe(false);
});

test('this test will not run', () => {
expect('A').toBe('A');
});

如果您有一個測試在作為較大套件的一部分執行時經常失敗,但單獨執行時不會失敗,那麼很可能來自不同測試的某些內容會干擾這個測試。您通常可以透過使用 beforeEach 清除一些共用狀態來修正此問題。如果您不確定是否修改了一些共用狀態,您也可以嘗試記錄資料的 beforeEach