跳至主要内容
版本: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 参数或返回一个 Promise。例如,如果 initializeCityDatabase() 返回一个在数据库初始化时解析的 Promise,则需要返回该 Promise

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

一次性设置

在某些情况下,您只需要在文件开头进行一次设置。当设置是异步的时,这尤其令人讨厌,因此您无法在内联中进行设置。Jest 提供了 beforeAllafterAll 钩子来处理这种情况。

例如,如果 initializeCityDatabase()clearCityDatabase() 都返回 Promise,并且城市数据库可以在测试之间重复使用,则可以将测试代码更改为

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);
});
});

请注意,顶层 beforeEachdescribe 块内的 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