Async setup и teardown

В Jasmine для тестирования асинхронного кода предусмотрены механизмы setup и teardown, позволяющие выполнять подготовку и очистку окружения до и после выполнения тестов. Основными функциями являются beforeEach, afterEach, beforeAll и afterAll. Они поддерживают асинхронные операции через callback, Promise или async/await.

beforeEach и afterEach

beforeEach выполняется перед каждым it в текущем describe, а afterEach — после каждого. Для асинхронного кода можно использовать три подхода:

  1. Callback через параметр done
beforeEach(function(done) {
    setTimeout(function() {
        // подготовка данных
        done(); // сигнал Jasmine о завершении
    }, 100);
});

afterEach(function(done) {
    setTimeout(function() {
        // очистка данных
        done();
    }, 100);
});
  • Параметр done обязателен.
  • Не вызов done приведет к тайм-ауту теста.
  1. Возврат Promise
beforeEach(() => {
    return fetchDataFromServer().then(data => {
        this.data = data;
    });
});

afterEach(() => {
    return cleanupResources();
});
  • Jasmine распознает Promise и ожидает его разрешения.
  • Позволяет избегать вложенных callback-функций.
  1. Использование async/await
beforeEach(async () => {
    this.data = await fetchDataFromServer();
});

afterEach(async () => {
    await cleanupResources();
});
  • Наиболее современный и читаемый способ.
  • Код становится линейным, проще управлять ошибками через try/catch.

beforeAll и afterAll

beforeAll выполняется один раз перед всеми тестами внутри describe, afterAll — один раз после всех тестов. Асинхронные возможности идентичны beforeEach/afterEach.

Пример:

beforeAll(async () => {
    this.server = await startTestServer();
});

afterAll(async () => {
    await this.server.stop();
});
  • Используется для дорогих операций инициализации, которые не нужно повторять перед каждым тестом.
  • Подходит для создания глобальных ресурсов, подключения к базе данных или запуске тестового сервера.

Важные моменты

  • Контекст this сохраняется внутри функций function() {}. Использование стрелочных функций (() => {}) может изменить контекст, поэтому при доступе к this внутри beforeEach или afterEach рекомендуется использовать обычные функции.
  • Тайм-аут по умолчанию для асинхронного теста или beforeEach/afterEach5 секунд. Можно изменить с помощью jasmine.DEFAULT_TIMEOUT_INTERVAL.
  • Ошибки в асинхронном коде корректно передаются в Jasmine, если используется done.fail(err) или отклонённый Promise.
beforeEach((done) => {
    fetchData().then(data => {
        this.data = data;
        done();
    }).catch(err => done.fail(err));
});

Примеры сочетания разных подходов

describe('Асинхронная подготовка и очистка', () => {
    beforeAll(async () => {
        this.globalResource = await initializeGlobalResource();
    });

    beforeEach((done) => {
        setupLocalData(this.globalResource).then(() => done());
    });

    it('тест с асинхронными операциями', async () => {
        const result = await performAsyncTask(this.globalResource);
        expect(result).toBe(true);
    });

    afterEach(() => cleanupLocalData(this.globalResource));

    afterAll(async () => {
        await this.globalResource.shutdown();
    });
});
  • В примере комбинируются async/await и done для демонстрации гибкости.
  • Локальные данные создаются и удаляются для каждого теста, глобальный ресурс инициализируется один раз.

Рекомендации по использованию

  • Для единичных тестов предпочтительнее beforeEach/afterEach с async/await.
  • Для глобальных ресурсов и тяжёлых инициализаций — beforeAll/afterAll.
  • Всегда обрабатывать ошибки в асинхронных функциях через try/catch или catch у Promise, чтобы Jasmine корректно регистрировал неудачные тесты.
  • Стараться не смешивать callback и Promise в одном блоке, чтобы не возникало неожиданного поведения.

Асинхронные setup и teardown позволяют создавать устойчивые, предсказуемые тестовые окружения, исключая состояние между тестами и упрощая работу с внешними ресурсами. Это критически важно для тестирования приложений с сетью, базой данных или любыми асинхронными зависимостями.