设置和拆卸
在编写测试时,您经常需要在测试运行之前进行一些设置工作,并在测试运行之后进行一些收尾工作。Jest 提供了辅助函数来处理此问题。
重复设置
如果您有一些需要为许多测试重复执行的工作,可以使用 beforeEach
和 afterEach
钩子。
例如,假设有几个测试与城市数据库交互。您有一个方法 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();
});
beforeEach
和 afterEach
可以像 测试处理异步代码 一样处理异步代码 - 它们可以接受 done
参数或返回一个 Promise。例如,如果 initializeCityDatabase()
返回一个在数据库初始化时解析的 Promise,则需要返回该 Promise
beforeEach(() => {
return initializeCityDatabase();
});
一次性设置
在某些情况下,您只需要在文件开头进行一次设置。当设置是异步的时,这尤其令人讨厌,因此您无法在内联中进行设置。Jest 提供了 beforeAll
和 afterAll
钩子来处理这种情况。
例如,如果 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);
});
});
请注意,顶层 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
就像 describe
和 test
块一样,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
。