Настройка и очистка
При написании тестов часто требуется выполнить настройку перед их запуском, а после завершения — выполнить очистку. Для этого в 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 или возвращать промис. Например, если бы initializeCityDatabase() возвращал промис, который разрешается после инициализации базы данных, нам следовало бы вернуть этот промис:
beforeEach(() => {
return initializeCityDatabase();
});
Однократная настройка
В некоторых случаях настройку нужно выполнить всего один раз — в начале файла. Это может быть особенно неудобно, если настройка асинхронная и её нельзя выполнить прямо в коде. Для таких ситуаций в Jest предусмотрены хуки beforeAll и afterAll.
Например, если бы 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 верхнего уровня выполняется перед beforeEach внутри блока describe. Порядок выполнения всех хуков можно проиллюстрировать следующим образом.
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 для вывода данных в журнал.
© 2022 Facebook, Inc.
Licensed under the MIT License.
https://jestjs.io/docs/setup-teardown