Функции beforeAll и afterAll

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


Синтаксис и базовое использование

beforeAll вызывается один раз перед выполнением всех тестов внутри блока describe.

describe('Пример beforeAll', () => {
  let data;

  beforeAll(() => {
    data = { message: 'Инициализация' };
    console.log('Выполняется один раз перед всеми тестами');
  });

  it('тест 1', () => {
    expect(data.message).toBe('Инициализация');
  });

  it('тест 2', () => {
    expect(data.message).toBe('Инициализация');
  });
});

afterAll вызывается один раз после выполнения всех тестов внутри блока describe.

describe('Пример afterAll', () => {
  let resource;

  beforeAll(() => {
    resource = { connected: true };
  });

  it('тест с использованием ресурса', () => {
    expect(resource.connected).toBeTrue();
  });

  afterAll(() => {
    resource.connected = false;
    console.log('Очистка ресурсов после всех тестов');
  });
});

Отличие от beforeEach и afterEach

  • beforeEach выполняется перед каждым тестом в блоке describe.
  • afterEach выполняется после каждого теста.
  • beforeAll и afterAll выполняются только один раз вне зависимости от количества тестов.

Использование beforeAll и afterAll снижает нагрузку на систему, когда подготовка или очистка ресурсов требует значительных затрат.

describe('Сравнение beforeAll и beforeEach', () => {
  let counter = 0;

  beforeAll(() => {
    counter += 10;
  });

  beforeEach(() => {
    counter += 1;
  });

  it('тест 1', () => {
    expect(counter).toBe(11); // 10 + 1
  });

  it('тест 2', () => {
    expect(counter).toBe(12); // 11 + 1
  });
});

Асинхронные операции

beforeAll и afterAll поддерживают асинхронный код через Promise или async/await. Это полезно для работы с базами данных, внешними API или таймерами.

describe('Асинхронные beforeAll и afterAll', () => {
  let data;

  beforeAll(async () => {
    data = await fetchDataFromServer(); // пример асинхронного вызова
  });

  it('тест с асинхронными данными', () => {
    expect(data).toBeDefined();
  });

  afterAll(async () => {
    await cleanUpServerData(data.id);
  });
});

Если beforeAll не завершится успешно, тесты не будут выполняться, а Jasmine выдаст ошибку. Аналогично, ошибки в afterAll влияют на отчёт о завершении тестов.


Вложенные блоки describe

beforeAll и afterAll могут использоваться в вложенных блоках describe, при этом порядок выполнения строго подчинён иерархии:

  1. Выполняется beforeAll внешнего блока.
  2. Выполняется beforeAll вложенного блока.
  3. Выполняются тесты вложенного блока.
  4. Выполняется afterAll вложенного блока.
  5. Выполняется afterAll внешнего блока.
describe('Внешний блок', () => {
  beforeAll(() => console.log('Внешний beforeAll'));

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

    it('тест', () => console.log('Тест'));
    
    afterAll(() => console.log('Внутренний afterAll'));
  });

  afterAll(() => console.log('Внешний afterAll'));
});

Результат консоли:

Внешний beforeAll
Внутренний beforeAll
Тест
Внутренний afterAll
Внешний afterAll

Практические рекомендации

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

Примеры использования с реальными ресурсами

  1. Инициализация базы данных один раз перед всеми тестами:
describe('Тесты с базой данных', () => {
  let db;

  beforeAll(async () => {
    db = await connectToDatabase();
  });

  it('получение данных', async () => {
    const users = await db.getUsers();
    expect(users.length).toBeGreaterThan(0);
  });

  afterAll(async () => {
    await db.disconnect();
  });
});
  1. Настройка глобальных объектов или конфигураций:
describe('Глобальная конфигурация', () => {
  let config;

  beforeAll(() => {
    config = loadGlobalConfig();
  });

  it('конфигурация содержит API ключ', () => {
    expect(config.apiKey).toBeDefined();
  });

  afterAll(() => {
    config = null; // очистка
  });
});

Использование beforeAll и afterAll делает тесты быстрее, чище и предсказуемее, особенно при работе с дорогими или глобальными ресурсами.