Setup and Teardown
Часто при написании тестов вам нужно проделать некоторую работу до того, как запустится тест, и некоторую работу по его завершению. 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
and afterEach
могут работать с асинхронным кодом также, как это делают асинхронные тесты - они могут либо принимать функцию done
в качестве параметра, либо возвращать promise. К примеру, if 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();
});
Определение контекста
По умолчанию, блоки before
и after
применяются к каждому тесту в файле. Можно группировать тесты вместе, используя блок describe
. Для тестов внутри describe
блока, блоки before
и after
применяются только для тестов внутри этого блока describe
.
For example, let's say we had not just a city database, but also a food database. We could do different setup for different tests:
// 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 sausage', () => {
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. It may help to illustrate the order of execution of all hooks.
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).toEqual(true);
});
});
console.log('describe outer-b');
test('test 1', () => {
console.log('test for describe outer');
expect(true).toEqual(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).toEqual(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
для записи логов выполнения.