跳转至主内容
版本:29.7

安装和移除

写测试的时候你经常需要在运行测试前做一些准备工作,和在运行测试后进行一些收尾工作。 Jest 提供辅助函数来处理这个问题。

Repeating Setup

If you have some work you need to do repeatedly for many tests, you can use beforeEach and afterEach hooks.

例如,我们考虑一些与城市信息数据库进行交互的测试。 You have a method initializeCityDatabase() that must be called before each of these tests, and a method clearCityDatabase() that must be called after each of these tests. 你可以这样做:

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 and afterEach can handle asynchronous code in the same ways that tests can handle asynchronous code - they can either take a done parameter or return a promise. For example, if initializeCityDatabase() returned a promise that resolved when the database was initialized, we would want to return that promise:

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

一次性设置

在某些情况下,你只需要在文件的开头做一次设置。 如果这个通用设置是异步的,就比较麻烦,因为没办法每个用例都设置一遍,这样性能还很差。 Jest provides beforeAll and afterAll hooks to handle this situation.

For example, if both initializeCityDatabase() and clearCityDatabase() returned promises, and the city database could be reused between tests, we could change our test code to:

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

作用域

The top level before* and after* hooks apply to every test in a file. The hooks declared inside a describe block apply only to the tests within that describe block.

比如说,我们不仅有一个城市的数据库,还有一个食品数据库。 我们可以为不同的测试做不同的设置︰

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

Note that the top-level beforeEach is executed before the beforeEach inside the describe block. 下面的代码也许能帮助你理解所有 hook 的执行顺序。

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 executes all describe handlers in a test file before it executes any of the actual tests. This is another reason to do setup and teardown inside before* and after* handlers rather than inside the describe blocks. Once the describe blocks are complete, by default Jest runs all the tests serially in the order they were encountered in the collection phase, waiting for each to finish and be tidied up before moving on.

考虑以下示例测试文件和输出:

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

Just like the describe and test blocks Jest calls the before* and after* hooks in the order of declaration. Note that the after* hooks of the enclosing scope are called first. For example, here is how you can set up and tear down resources which depend on each other:

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
备注

If you are using jasmine2 test runner, take into account that it calls the after* hooks in the reverse order of declaration. To have identical output, the above example should be altered like this:

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

// ...

通用建议

如果测试失败,第一件要检查的事就是,当仅运行这条测试时,它是否仍然失败。 To run only one test with Jest, temporarily change that test command to a 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');
});

如果你有一个测试,当它作为一个更大的用例中的一部分时,经常运行失败,但是当你单独运行它时,并不会失败,所以最好考虑其他测试对这个测试的影响。 You can often fix this by clearing some shared state with beforeEach. If you're not sure whether some shared state is being modified, you can also try a beforeEach that logs data.