Порядок выполнения хуков

Jasmine предоставляет механизм хуков, который позволяет выполнять код до и после тестов, а также перед и после групп тестов. Понимание порядка их выполнения критически важно для корректной организации тестовой среды и предотвращения нежелательных побочных эффектов.

Основные хуки

Jasmine имеет четыре базовых типа хуков:

  • beforeAll(fn): выполняется один раз перед всеми тестами в текущем describe блоке. Используется для глобальной подготовки данных, создания объектов, настройки состояния, которое будет общим для всех тестов внутри блока.
  • beforeEach(fn): выполняется перед каждым тестом (it) внутри текущего describe. Идеально подходит для восстановления состояния или создания свежих объектов для каждого теста.
  • afterEach(fn): выполняется после каждого теста. Используется для очистки ресурсов, сброса данных или отмены побочных эффектов, оставшихся после теста.
  • afterAll(fn): выполняется один раз после всех тестов в текущем describe блоке. Применяется для окончательной очистки ресурсов, закрытия соединений или освобождения памяти.

Вложенные блоки describe и наследование хуков

Jasmine поддерживает вложенность блоков describe. Порядок выполнения хуков в этом случае подчиняется иерархии блоков:

  1. Вызов beforeAll начинается с внешнего блока и спускается к внутренним.
  2. Вызов beforeEach идет от внешнего блока к внутреннему перед выполнением каждого теста.
  3. Выполнение теста (it).
  4. Вызов afterEach идет в обратном порядке: от внутреннего блока к внешнему после каждого теста.
  5. Вызов afterAll начинается с внутреннего блока и поднимается к внешнему после выполнения всех тестов внутри блока.

Пример:

describe('Внешний блок', () => {
  beforeAll(() => console.log('beforeAll внешнего'));
  beforeEach(() => console.log('beforeEach внешнего'));
  afterEach(() => console.log('afterEach внешнего'));
  afterAll(() => console.log('afterAll внешнего'));

  it('тест 1', () => console.log('тест 1'));

  describe('Внутренний блок', () => {
    beforeAll(() => console.log('beforeAll внутреннего'));
    beforeEach(() => console.log('beforeEach внутреннего'));
    afterEach(() => console.log('afterEach внутреннего'));
    afterAll(() => console.log('afterAll внутреннего'));

    it('тест 2', () => console.log('тест 2'));
  });
});

Порядок выполнения в консоли:

beforeAll внешнего
beforeEach внешнего
тест 1
afterEach внешнего
beforeAll внутреннего
beforeEach внешнего
beforeEach внутреннего
тест 2
afterEach внутреннего
afterEach внешнего
afterAll внутреннего
afterAll внешнего

Асинхронные хуки

Jasmine поддерживает асинхронные операции в хуках. Любой хук может вернуть Promise или использовать async/await:

beforeEach(async () => {
  await fetchData();
});

afterEach(async () => {
  await cleanupData();
});

Асинхронные хуки блокируют выполнение теста до завершения их работы, обеспечивая правильный порядок подготовки и очистки.

Влияние хуков на стабильность тестов

  • Изоляция тестов: использование beforeEach и afterEach гарантирует, что каждый тест работает с независимым состоянием.
  • Оптимизация времени: использование beforeAll и afterAll позволяет избежать повторной инициализации ресурсов, которые не меняются между тестами.
  • Понимание последовательности предотвращает ошибки при сложных вложенных структурах и уменьшает риск неожиданных побочных эффектов.

Лучшие практики

  • Использовать beforeAll и afterAll для дорогостоящих операций, которые не меняются между тестами.
  • Использовать beforeEach и afterEach для чистого состояния перед каждым тестом.
  • Во вложенных блоках describe явно учитывать порядок хуков, чтобы избежать конфликтов между внутренними и внешними контекстами.
  • Всегда тестировать асинхронные хуки на корректность выполнения, чтобы избежать зависаний тестового раннера.

Понимание и правильное использование хуков в Jasmine позволяет создавать устойчивые, предсказуемые и легко поддерживаемые тесты, а также управлять сложными сценариями тестирования с минимальными ошибками и конфликтами.