Вызов оригинальной функции через and.callThrough

В JavaScript-тестировании с использованием Jasmine часто возникает необходимость создавать шпионов (spies) для функций. Основное предназначение шпионов — отслеживание вызовов функции, аргументов, возвращаемых значений и количества вызовов. Однако в ряде случаев требуется не просто контролировать вызовы функции, а сохранять её оригинальное поведение, при этом имея возможность анализировать, как и с какими аргументами она вызывается. Для этой цели используется метод and.callThrough.


Основные возможности and.callThrough

Метод and.callThrough позволяет шпиону проксиировать вызовы оригинальной функции, сохраняя при этом её стандартную реализацию. Другими словами, вызов функции через шпион с callThrough выполняет то же, что и сама функция, но при этом Jasmine фиксирует:

  • количество вызовов,
  • переданные аргументы,
  • возвращаемое значение.

Это особенно полезно, когда:

  1. Необходимо проверить взаимодействие разных модулей через конкретные функции.
  2. Требуется сохранить поведение функции, чтобы тест оставался правдоподобным.
  3. Нужно комбинировать мониторинг и реальное выполнение кода.

Синтаксис использования

spyOn(object, 'method').and.callThrough();
  • object — объект, содержащий функцию.
  • 'method' — название функции, которую требуется шпионить.
  • callThrough() — указывает шпиону вызывать оригинальную реализацию функции.

Пример:

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

describe('Тестирование add с callThrough', function() {
  it('должен вызывать оригинальную функцию', function() {
    spyOn(calculator, 'add').and.callThrough();

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

    expect(calculator.add).toHaveBeenCalled();
    expect(calculator.add).toHaveBeenCalledWith(2, 3);
    expect(result).toBe(5);
  });
});

В этом примере шпион фиксирует вызов функции add, проверяет аргументы и одновременно позволяет функции выполнить своё стандартное поведение.


Отличие от других методов шпиона

Jasmine предоставляет несколько способов подмены функций через шпионы:

  1. and.returnValue(value) — всегда возвращает указанное значение, оригинальная функция не вызывается.
  2. and.callFake(fn) — позволяет заменить функцию своей реализацией.
  3. and.stub() — создает пустой шпион без вызова оригинальной функции.
  4. and.callThrough() — вызывает оригинальную функцию, сохраняя её стандартное поведение.

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


Использование в сложных сценариях

Комбинирование с beforeEach

Шпионы с callThrough часто создаются в блоках beforeEach, чтобы каждый тест начинался с чистой конфигурации:

describe('Calculator', function() {
  beforeEach(function() {
    spyOn(calculator, 'add').and.callThrough();
  });

  it('правильно суммирует числа', function() {
    expect(calculator.add(1, 2)).toBe(3);
  });

  it('функция вызвана дважды', function() {
    calculator.add(3, 4);
    calculator.add(5, 6);

    expect(calculator.add).toHaveBeenCalledTimes(2);
  });
});

Сложные объекты и методы

callThrough применим даже для методов объектов с внутренним состоянием:

const counter = {
  count: 0,
  increment: function() {
    this.count += 1;
    return this.count;
  }
};

describe('Counter', function() {
  beforeEach(function() {
    spyOn(counter, 'increment').and.callThrough();
  });

  it('увеличивает счетчик', function() {
    counter.increment();
    counter.increment();

    expect(counter.increment).toHaveBeenCalledTimes(2);
    expect(counter.count).toBe(2);
  });
});

Здесь шпион фиксирует вызовы метода, но сохраняет изменение внутреннего состояния объекта, что невозможно при использовании and.stub() или and.returnValue().


Преимущества and.callThrough

  • Позволяет следить за вызовами функций без изменения их логики.
  • Удобно использовать при интеграционном тестировании модулей.
  • Совместимо с другими проверками Jasmine: toHaveBeenCalled, toHaveBeenCalledWith, toHaveBeenCalledTimes.
  • Позволяет тестировать функции с побочными эффектами, не отключая их.

Советы по применению

  1. Использовать callThrough, когда важно сохранить оригинальную логику функции.
  2. Для функций, которые вызывают сторонние сервисы или имеют непредсказуемые побочные эффекты, лучше заменять их на callFake или returnValue.
  3. Комбинировать callThrough с асинхронными тестами можно, если функция возвращает промис или выполняет коллбэк:
spyOn(api, 'fetchData').and.callThrough();

api.fetchData().then(response => {
  expect(response).toBeDefined();
});
  1. Следить за тем, чтобы шпион не мешал работе функции. callThrough подходит только для безопасных методов, которые можно вызывать в тестовой среде.

and.callThrough является мощным инструментом Jasmine, позволяющим контролировать и наблюдать за функцией, сохраняя её естественное поведение, что делает тесты более реалистичными и наглядными.