Jest: beforeEach, afterEach, beforeAll, afterAll
Хуки жизненного цикла Jest — функции, которые запускаются автоматически до или после каждого теста (или всего suite), позволяя управлять setup и teardown.
Зачем нужно
Повторяющийся код инициализации (создание объектов, мок-данные, открытие соединений) нужно вынести из отдельных тестов. Хуки гарантируют, что каждый тест получает чистое состояние и после завершения освобождает ресурсы — это предотвращает утечки и взаимное влияние тестов.
Где используется
- Инициализация объектов, хранилищ или экземпляров классов перед каждым тестом
- Сброс моков и счётчиков между тестами (
jest.clearAllMocks) - Открытие и закрытие соединения с БД или файловой системой
- Настройка глобального состояния (window, document) в jsdom-окружении
Основной контент
Порядок выполнения
beforeAll → beforeEach → test → afterEach → afterEach → ... → afterAll
beforeAll(fn)— выполняется один раз перед всеми тестами в блокеdescribebeforeEach(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 и тесты не завершатся