Skip to main content
Version: 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 在执行任何实际测试之前会执行测试文件中的所有描述处理程序。 这是在 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