Организация тестовых наборов

Jasmine — это популярный фреймворк для тестирования JavaScript, обеспечивающий чистый синтаксис и поддержку поведения, ориентированного на тестирование (Behavior-Driven Development, BDD). Ключевым элементом в организации тестов являются тестовые наборы, которые позволяют структурировать тесты, группировать их по функциональности и управлять зависимостями между ними.


1. describe и иерархия тестов

Основным строительным блоком для организации тестов в Jasmine является функция describe. Она создает тестовый набор (suite), который может содержать несколько спеков (specs), реализованных через функцию it.

describe('Массивы', () => {
    it('должен содержать добавленный элемент', () => {
        const arr = [];
        arr.push(1);
        expect(arr).toContain(1);
    });

    it('должен иметь длину 0 при инициализации', () => {
        const arr = [];
        expect(arr.length).toBe(0);
    });
});

Особенности describe:

  • Может содержать вложенные describe, создавая иерархическую структуру тестов.
  • Позволяет группировать тесты по функциональности, модулям или компонентам.
  • Использует общие хуки (beforeEach, afterEach) для подготовки состояния перед запуском всех спеков внутри набора.

2. Вложенные тестовые наборы

Jasmine поддерживает вложение describe внутри другого describe. Это позволяет строить детализированные сценарии и логически разделять тесты.

describe('Калькулятор', () => {
    describe('Сложение', () => {
        it('должно правильно складывать положительные числа', () => {
            expect(1 + 2).toBe(3);
        });

        it('должно правильно складывать отрицательные числа', () => {
            expect(-1 + -2).toBe(-3);
        });
    });

    describe('Вычитание', () => {
        it('должно правильно вычитать положительные числа', () => {
            expect(5 - 2).toBe(3);
        });
    });
});

Преимущества вложенных наборов:

  • Четкая структура тестов по функциональным блокам.
  • Возможность наследования хуков: beforeEach родительского describe выполняется перед хуками дочернего уровня.

3. Хуки для подготовки и очистки

Jasmine предоставляет четыре основных типа хуков, используемых внутри describe:

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

Пример применения хуков:

describe('Пользовательский сервис', () => {
    let service;

    beforeAll(() => {
        service = new UserService();
    });

    beforeEach(() => {
        service.clearUsers();
    });

    it('должен добавлять нового пользователя', () => {
        service.addUser('Alice');
        expect(service.getUsers()).toContain('Alice');
    });

    it('должен возвращать пустой список после очистки', () => {
        expect(service.getUsers().length).toBe(0);
    });
});

Особенности:

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

4. Фокусировка и пропуск тестов

Jasmine поддерживает механизмы временной фокусировки или исключения тестов:

  • fdescribe — выполняет только этот набор тестов, игнорируя все остальные.
  • xdescribe — пропускает весь набор тестов.
  • fit — выполняет только один тест.
  • xit — пропускает отдельный тест.

Пример:

fdescribe('Фокус на сложении', () => {
    it('проверка 2 + 2', () => {
        expect(2 + 2).toBe(4);
    });
});

xdescribe('Пропущенные тесты', () => {
    it('этот тест не выполняется', () => {});
});

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


5. Организация больших проектов

Для крупных проектов рекомендуется использовать следующие подходы к организации тестовых наборов:

  • Модульная структура: отдельные describe для каждого модуля или компонента.
  • Файловая структура: соответствие файлов тестов с файловой структурой приложения (app/components/Button.jsspec/components/Button.spec.js).
  • Общие хуки: создание утилит и базовых конфигураций для повторного использования в разных наборах.
  • Изоляция состояния: каждый набор тестов должен создавать свое состояние, избегая влияния на другие наборы.

6. Использование фабрик и утилит

Для организации тестов удобно создавать фабрики объектов и утилитные функции, чтобы уменьшить дублирование кода внутри beforeEach:

function createUser(name = 'Default') {
    return { name, createdAt: new Date() };
}

describe('User Factory', () => {
    let user;

    beforeEach(() => {
        user = createUser('Alice');
    });

    it('должен иметь правильное имя', () => {
        expect(user.name).toBe('Alice');
    });

    it('должен содержать дату создания', () => {
        expect(user.createdAt).toBeDefined();
    });
});

Такой подход повышает читаемость тестов и делает наборы более поддерживаемыми и масштабируемыми.


7. Совмещение с асинхронными операциями

Jasmine поддерживает тестирование асинхронного кода через done callback, промисы или async/await. Хуки также могут быть асинхронными:

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

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

    it('должен загружать данные', () => {
        expect(data).not.toBeNull();
    });
});

Использование асинхронных хуков обеспечивает корректное выполнение тестов, которые зависят от внешних ресурсов, без нарушения изоляции наборов.


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