Создание spy с помощью spyOn

В тестировании JavaScript с использованием Jasmine одной из ключевых возможностей является наблюдение за вызовами функций и подмена их поведения без изменения исходного кода. Для этого применяется встроенный механизм spy.

spyOn — это метод Jasmine, который позволяет шпионить за существующими методами объектов. Он предоставляет полный контроль над вызовами функции, позволяет проверять параметры, количество вызовов и подменять реализацию.


Основной синтаксис spyOn

spyOn(object, 'methodName');
  • object — объект, содержащий метод, который нужно «шпионить».
  • methodName — строка с именем метода объекта.

После применения spyOn, оригинальная функция заменяется специальной «шпионской» функцией, которая сохраняет информацию о вызовах.

Пример:

const calculator = {
  add(a, b) {
    return a + b;
  }
};

spyOn(calculator, 'add');

calculator.add(2, 3);

expect(calculator.add).toHaveBeenCalled();
expect(calculator.add).toHaveBeenCalledWith(2, 3);

В этом примере spyOn отслеживает вызов метода add и его аргументы. Оригинальный результат функции по умолчанию не выполняется, если не указана реализация через методы spy.


Подмена реализации метода

spyOn позволяет изменять поведение функции, чтобы тестирование не зависело от оригинальной реализации.

  • and.callThrough() — сохраняет оригинальное поведение функции, одновременно позволяя отслеживать вызовы:
spyOn(calculator, 'add').and.callThrough();

const result = calculator.add(2, 3);

expect(result).toBe(5);
expect(calculator.add).toHaveBeenCalled();
  • and.returnValue(value) — всегда возвращает указанное значение:
spyOn(calculator, 'add').and.returnValue(10);

const result = calculator.add(2, 3);

expect(result).toBe(10);
  • and.callFake(fakeFunction) — заменяет функцию своей реализацией:
spyOn(calculator, 'add').and.callFake((a, b) => a * b);

expect(calculator.add(2, 3)).toBe(6);
  • and.stub() — создает пустую функцию, которая ничего не делает и возвращает undefined. Используется для подавления побочных эффектов:
spyOn(calculator, 'add').and.stub();

expect(calculator.add(2, 3)).toBeUndefined();

Проверка вызовов spy

Jasmine предоставляет набор удобных матчеров для spy:

  • toHaveBeenCalled() — проверяет, был ли вызов функции.
  • toHaveBeenCalledTimes(number) — проверяет количество вызовов.
  • toHaveBeenCalledWith(...args) — проверяет, вызывалась ли функция с указанными аргументами.
  • toHaveBeenCalledOnceWith(...args) — проверяет, что функция вызвалась один раз с указанными аргументами.
  • toHaveBeenCalledBefore(spy) / toHaveBeenCalledAfter(spy) — проверяет порядок вызовов между spy.

Пример проверки нескольких вызовов:

calculator.add(1, 2);
calculator.add(3, 4);

expect(calculator.add).toHaveBeenCalledTimes(2);
expect(calculator.add).toHaveBeenCalledWith(3, 4);

Взаимодействие с асинхронными функциями

spyOn поддерживает асинхронные функции и промисы. Для работы с async/await или функциями, возвращающими промис, можно использовать and.callFake:

const api = {
  fetchData() {
    return fetch('/data');
  }
};

spyOn(api, 'fetchData').and.callFake(() => Promise.resolve({ data: 123 }));

await api.fetchData().then(result => {
  expect(result.data).toBe(123);
});

Это позволяет тестировать код без реальных сетевых запросов.


Ограничения spyOn

  1. Можно шпионить только существующие методы объекта. Нельзя использовать spyOn на переменные, функции вне объектов или свойства, не являющиеся функцией.
  2. Не работает с функциями класса, если метод не является экземпляром объекта, но для классов можно использовать spyOn(Class.prototype, 'method').

Пример для классов:

class User {
  greet(name) {
    return `Hello, ${name}!`;
  }
}

spyOn(User.prototype, 'greet');

const user = new User();
user.greet('Alice');

expect(user.greet).toHaveBeenCalledWith('Alice');

Советы по использованию spyOn

  • Использовать spyOn для разделения теста и зависимости. Например, чтобы не вызывать сетевые запросы или изменения базы данных.
  • Применять and.callThrough() только когда нужно протестировать и вызов, и результат функции.
  • Всегда очищать spy после тестов, если один и тот же объект используется повторно, чтобы не возникали конфликты.
afterEach(() => {
  jasmine.restoreAllSpies();
});

spyOn — это универсальный инструмент для мониторинга, контроля и подмены поведения функций, обеспечивающий детализированное тестирование JavaScript-приложений без изменения исходного кода. Правильное применение spy позволяет создавать надежные и изолированные тесты, полностью контролируя вызовы и их результаты.