Подготовка и очистка
Часто при написании тестов вам нужно проделать некоторую работу до того, как запустится тест, и некоторую работу по его завершению. 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, который вызывает resolve, когда база данных инициализирована, нам бы хотелось вернуть этот 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();
});
Определение контекста
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.
К примеру, допустим у нас есть не только база городов, но и база продовольствия. Мы могли бы организовать различную подготовку к разным тестам:
// Применяется ко всем тестам в этом файле
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', () => {
// Применяется только к тестам в этом describe блоке
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
Последовательность выполнения блоков describe и test
Jest выполняет все обработчики describe
внутри одного файла до того, как будет запущен какой-либо тест. Это еще одна причина, чтобы проводить подготовительные и завершающие работы внутри обработчиков before*
и after*
, вместо того, чтобы описывать их внутри блоков describe
. Как только завершатся все describe
блоки, по умолчанию Jest запустит все тесты последовательно в том порядке, в котором они были обнаружены на этапе сбора, ожидая, пока каждый из них завершится и будет убран, прежде чем двигаться дальше.
Рассмотрим следующий пример тестового файла и результат его выполнения:
describe('outer', () => {
console.log('describe outer-a');
describe('describe inner 1', () => {
console.log('describe inner 1');
test('test 1', () => {
console.log('test for describe inner 1');
expect(true).toBe(true);
});
});
console.log('describe outer-b');
test('test 1', () => {
console.log('test for describe outer');
expect(true).toBe(true);
});
describe('describe inner 2', () => {
console.log('describe inner 2');
test('test for describe inner 2', () => {
console.log('test for describe inner 2');
expect(false).toBe(false);
});
});
console.log('describe outer-c');
});
// describe outer-a
// describe inner 1
// describe outer-b
// describe inner 2
// describe outer-c
// test for describe inner 1
// test for describe outer
// test for describe inner 2
Общие рекомендации
Если тест падает, в первую очередь нужно проверить, что он падает, будучи запущенным в одиночку. В 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
для записи логов выполнения.