Extending Jasmine API

Jasmine предоставляет мощный набор инструментов для тестирования JavaScript-кода. Однако, иногда стандартных функций бывает недостаточно для реализации специфических тестов или удобства работы. В таких случаях возникает необходимость в расширении API Jasmine. Этот процесс позволяет добавлять кастомные матчеры, обработчики или вспомогательные методы, которые улучшат качество и читабельность тестов.

Расширение через кастомные матчеры

Одним из самых распространённых способов расширения Jasmine является добавление кастомных матчеров. В Jasmine матчер — это метод, который используется для проверки значений в тестах. Стандартный набор матчеров предоставляет возможности для проверки равенства, истинности, типа и других базовых условий. Тем не менее, в некоторых случаях может потребоваться создание своих собственных матчеров.

Создание кастомного матчера

Для добавления кастомного матчера в Jasmine необходимо использовать метод addMatchers. Этот метод позволяет зарегистрировать новый матчеры, которые будут доступны в тестах. Структура кастомного матчера выглядит следующим образом:

beforeEach(function() {
    jasmine.addMatchers({
        toBeEven: function() {
            return {
                compare: function(actual) {
                    const result = {};
                    result.pass = actual % 2 === 0;
                    result.message = actual + (result.pass ? " is even" : " is not even");
                    return result;
                }
            };
        }
    });
});

it('should be an even number', function() {
    expect(4).toBeEven();
});

В примере выше создаётся матчтер toBeEven, который проверяет, является ли число чётным. Метод compare возвращает объект с двумя свойствами: pass (логическое значение, показывающее, прошёл ли тест) и message (сообщение, которое выводится в случае ошибки).

Параметры кастомных матчеров
  • compare: это функция, которая принимает аргумент actual — значение, которое проверяется в тесте.
  • pass: логическое значение, показывающее результат проверки.
  • message: строка, которая выводится в случае ошибки.

Расширение через кастомные глобальные функции

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

Пример кастомной глобальной функции
beforeEach(function() {
    global.isPalindrome = function(str) {
        return str === str.split('').reverse().join('');
    };
});

it('should check for palindrome correctly', function() {
    expect(isPalindrome("racecar")).toBe(true);
    expect(isPalindrome("hello")).toBe(false);
});

В данном примере создаётся глобальная функция isPalindrome, которая проверяет, является ли строка палиндромом. Эта функция доступна во всех тестах, и её можно использовать для выполнения проверок, не повторяя код.

Расширение через кастомные хуки

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

Пример кастомного хука
beforeAll(function() {
    console.log('Setting up test environment...');
});

afterAll(function() {
    console.log('Cleaning up after tests...');
});

it('should pass a simple test', function() {
    expect(1 + 1).toBe(2);
});

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

Расширение через кастомные spyOn методы

Jasmine включает механизм шпионов (spies), который позволяет отслеживать вызовы методов и их параметры. Стандартный функционал Jasmine включает в себя такие методы, как spyOn, которые могут быть использованы для отслеживания и контроля вызова методов.

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

Пример кастомного шпиона
beforeEach(function() {
    spyOn(window, 'alert').and.callFake(function() {
        console.log('Custom alert triggered');
    });
});

it('should trigger custom alert', function() {
    alert('Hello World');
    expect(alert).toHaveBeenCalledWith('Hello World');
});

В этом примере создаётся шпион для глобальной функции alert. Вместо стандартного вызова alert, используется подмена поведения с помощью callFake. Таким образом, мы можем перехватывать вызовы и проверять их параметры, не вызывая фактическое всплывающее окно.

Использование jasmine.clock() для работы с асинхронностью

Для тестирования асинхронного кода, который зависит от времени, Jasmine предоставляет инструмент jasmine.clock(). Этот инструмент позволяет контролировать таймеры в тестах и ускорить тестирование асинхронных операций.

Пример использования jasmine.clock()
beforeEach(function() {
    jasmine.clock().install();
});

afterEach(function() {
    jasmine.clock().uninstall();
});

it('should delay execution', function() {
    let result = false;
    setTimeout(function() {
        result = true;
    }, 1000);
    
    jasmine.clock().tick(1000);
    expect(result).toBe(true);
});

В данном примере используется jasmine.clock().tick(), который заставляет тест продвигаться во времени, минуя задержки. Это позволяет проверять поведение кода, использующего таймеры, без фактического ожидания времени.

Расширение через кастомные ассерт-методы

Иногда возникает необходимость добавить новые методы для утверждений (assertions), которые не входят в стандартный набор Jasmine. Например, если необходимо проверять, что объект содержит все необходимые ключи или что строки соответствуют регулярному выражению.

Пример кастомного ассерт-метода
beforeEach(function() {
    jasmine.addMatchers({
        toContainKeys: function() {
            return {
                compare: function(actual, expected) {
                    const result = {};
                    result.pass = expected.every(key => key in actual);
                    result.message = result.pass 
                        ? `Object contains all expected keys`
                        : `Object is missing keys: ${expected.filter(key => !(key in actual)).join(', ')}`;
                    return result;
                }
            };
        }
    });
});

it('should contain all required keys', function() {
    const obj = { name: 'Alice', age: 25 };
    expect(obj).toContainKeys(['name', 'age']);
});

В этом примере создаётся кастомный матчтер toContainKeys, который проверяет, что объект содержит все переданные ключи.

Заключение

Расширение API Jasmine предоставляет гибкость и позволяет создавать более выразительные, удобные и специфичные тесты. Добавление кастомных матчеров, хуков, шпионов и других функций помогает упростить написание тестов, сделать их более читаемыми и улучшить поддержку сложных тестовых сценариев.