Jest: beforeEach, afterEach, beforeAll, afterAll

Хуки жизненного цикла Jest — функции, которые запускаются автоматически до или после каждого теста (или всего suite), позволяя управлять setup и teardown.

Зачем нужно

Повторяющийся код инициализации (создание объектов, мок-данные, открытие соединений) нужно вынести из отдельных тестов. Хуки гарантируют, что каждый тест получает чистое состояние и после завершения освобождает ресурсы — это предотвращает утечки и взаимное влияние тестов.

Где используется

  • Инициализация объектов, хранилищ или экземпляров классов перед каждым тестом
  • Сброс моков и счётчиков между тестами (jest.clearAllMocks)
  • Открытие и закрытие соединения с БД или файловой системой
  • Настройка глобального состояния (window, document) в jsdom-окружении

Основной контент

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

beforeAll → beforeEach → test → afterEach → afterEach → ... → afterAll
  • beforeAll(fn) — выполняется один раз перед всеми тестами в блоке describe
  • beforeEach(fn) — выполняется перед каждым тестом
  • afterEach(fn) — выполняется после каждого теста
  • afterAll(fn) — выполняется один раз после всех тестов

Пример: сброс состояния перед каждым тестом

// cart.test.js
import { Cart } from './cart';

let cart;

beforeEach(() => {
  cart = new Cart(); // каждый тест получает свежую корзину
});

test('добавляет товар', () => {
  cart.add({ id: 1, price: 100 });
  expect(cart.total).toBe(100);
});

test('удаляет товар', () => {
  cart.add({ id: 1, price: 100 });
  cart.remove(1);
  expect(cart.total).toBe(0);
});

Пример: beforeAll для дорогой инициализации

import { createDbConnection, closeDbConnection } from './db';

let db;

beforeAll(async () => {
  db = await createDbConnection; // соединение создаётся один раз
});

afterAll(async () => {
  await closeDbConnection(db); // освобождаем ресурс
});

beforeEach(async () => {
  await db.query('DELETE FROM users'); // очищаем таблицу перед каждым тестом
});

test('создаёт пользователя', async () => {
  await db.query("INSERT INTO users (name) VALUES ('Alice')");
  const users = await db.query('SELECT * FROM users');
  expect(users).toHaveLength(1);
});

Вложенные describe и область видимости

describe('внешний блок', () => {
  beforeEach( => console.log('outer beforeEach'));

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

    test('порядок', () => {
      // outer beforeEach → inner beforeEach → test
    });
  });
});

Сброс моков

import * as api from './api';

beforeEach(() => {
  jest.clearAllMocks; // сбрасывает вызовы, но не реализацию
  // jest.resetAllMocks  — сбрасывает реализацию тоже
  // jest.restoreAllMocks — восстанавливает оригинал после spyOn
});

Частые ошибки

  • Изменение общего состояния в beforeAll без сброса — один тест может сломать другой, если мутирует объект, созданный в beforeAll
  • Асинхронный хук без async/await — Jest не ждёт завершения, тест начинается до инициализации; всегда возвращай Promise или используй async/await
  • Слишком много логики в beforeEach — замедляет каждый тест; тяжёлые операции (DB, HTTP) лучше в beforeAll
  • Забытый afterAll при открытии соединений — Jest выведет предупреждение об открытых handles и тесты не завершатся

Связанные темы

Ресурсы