Получение информации о вызовах

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


Spy: основной инструмент для мониторинга вызовов

Spy в Jasmine — это объект, который «подставляется» вместо функции и регистрирует каждый её вызов, аргументы, количество вызовов и возвращаемые значения. Spy может быть создан:

let obj = {
    method: function(a, b) {
        return a + b;
    }
};

spyOn(obj, 'method');
obj.method(1, 2);

В этом примере:

  • spyOn(obj, 'method') превращает method в spy.
  • Любой последующий вызов obj.method() фиксируется.
  • Spy сохраняет информацию о количестве вызовов и переданных аргументах.

Основные методы для получения информации о вызовах

Jasmine предоставляет встроенные методы для анализа spy:

  1. toHaveBeenCalled() Проверяет, был ли spy вызван хотя бы один раз.

    expect(obj.method).toHaveBeenCalled();
  2. toHaveBeenCalledTimes(number) Сравнивает количество вызовов с ожидаемым числом.

    expect(obj.method).toHaveBeenCalledTimes(2);
  3. toHaveBeenCalledWith(...args) Проверяет, вызывался ли spy с указанными аргументами.

    expect(obj.method).toHaveBeenCalledWith(1, 2);
  4. calls Свойство spy, предоставляющее подробный интерфейс для анализа вызовов:

    • calls.any() — возвращает true, если spy был вызван хотя бы один раз.
    • calls.count() — возвращает количество вызовов.
    • calls.argsFor(index) — возвращает массив аргументов конкретного вызова по индексу.
    • calls.allArgs() — массив аргументов всех вызовов.
    • calls.all() — массив объектов CallInfo с полной информацией о каждом вызове.
    • calls.mostRecent() — объект CallInfo для последнего вызова.
    • calls.first() — объект CallInfo для первого вызова.
    • calls.reset() — сбрасывает историю вызовов.

Пример использования calls:

obj.method(1, 2);
obj.method(3, 4);

console.log(obj.method.calls.count()); // 2
console.log(obj.method.calls.argsFor(0)); // [1, 2]
console.log(obj.method.calls.mostRecent().args); // [3, 4]

Подмена поведения функции

Spy позволяет не только отслеживать вызовы, но и подменять реальное поведение функции:

  1. and.callThrough() Выполняет оригинальную функцию при вызове spy:

    spyOn(obj, 'method').and.callThrough();
    obj.method(1, 2); // вызов реальной функции
  2. and.returnValue(value) Возвращает указанное значение вместо оригинальной функции:

    spyOn(obj, 'method').and.returnValue(42);
    obj.method(1, 2); // всегда вернет 42
  3. and.callFake(fakeFunction) Позволяет заменить функцию пользовательской реализацией:

    spyOn(obj, 'method').and.callFake((a, b) => a * b);
    obj.method(3, 4); // вернет 12
  4. and.throwError(message) Подбрасывает ошибку при вызове spy:

    spyOn(obj, 'method').and.throwError('Ошибка вызова');
    expect(() => obj.method()).toThrowError('Ошибка вызова');

Проверка последовательности вызовов

Иногда важно не только знать, был ли вызов, но и порядок вызовов нескольких функций. Для этого можно использовать calls.all():

spyOn(obj, 'method1');
spyOn(obj, 'method2');

obj.method1();
obj.method2();
obj.method1();

const calls = obj.method1.calls.all();
console.log(calls.length); // 2
console.log(calls[0].args); // аргументы первого вызова method1

Важные нюансы использования

  • Spy заменяет функцию, поэтому без and.callThrough() оригинальная логика не выполняется. Это критично при тестировании побочных эффектов.
  • Сброс истории вызовов (calls.reset()) полезен при тестировании нескольких сценариев в одном it блоке.
  • Spy можно применять к глобальным функциям и объектам, что позволяет тестировать взаимодействие модулей без их реальной реализации.
  • Локальные функции в замыканиях нельзя напрямую «шпионить» через spyOn; требуется экспортировать функцию или использовать jasmine.createSpy().

Jasmine.createSpy

Для функций, которые не существуют в объекте, можно создать чистый spy:

let spyFunc = jasmine.createSpy('mySpy');
spyFunc(1, 2);

expect(spyFunc).toHaveBeenCalledWith(1, 2);

createSpy также поддерживает:

  • and.returnValue()
  • and.callFake()
  • and.throwError()

Применение в реальных тестах

Spy используется для:

  • Проверки вызова callback-функций.
  • Контроля взаимодействия компонентов.
  • Замены сетевых запросов и асинхронных операций.
  • Тестирования событий, обработчиков и побочных эффектов.

Пример проверки callback:

function fetchData(callback) {
    setTimeout(() => {
        callback('data');
    }, 100);
}

it('вызывает callback с данными', (done) => {
    const callback = jasmine.createSpy('callback').and.callFake((data) => {
        expect(data).toBe('data');
        done();
    });

    fetchData(callback);
});

Этот набор инструментов делает Jasmine мощным средством контроля и анализа вызовов функций, позволяя писать тесты, которые проверяют не только результат, но и взаимодействие компонентов в приложении.