В JavaScript-тестировании с использованием Jasmine часто возникает
необходимость создавать шпионов (spies) для функций.
Основное предназначение шпионов — отслеживание вызовов функции,
аргументов, возвращаемых значений и количества вызовов. Однако в ряде
случаев требуется не просто контролировать вызовы функции, а
сохранять её оригинальное поведение, при этом имея
возможность анализировать, как и с какими аргументами она вызывается.
Для этой цели используется метод and.callThrough.
and.callThroughМетод and.callThrough позволяет шпиону
проксиировать вызовы оригинальной функции, сохраняя при
этом её стандартную реализацию. Другими словами, вызов функции через
шпион с callThrough выполняет то же, что и сама
функция, но при этом Jasmine фиксирует:
Это особенно полезно, когда:
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 предоставляет несколько способов подмены функций через шпионы:
and.returnValue(value) — всегда
возвращает указанное значение, оригинальная функция не вызывается.and.callFake(fn) — позволяет заменить
функцию своей реализацией.and.stub() — создает пустой шпион без
вызова оригинальной функции.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.callThroughtoHaveBeenCalled, toHaveBeenCalledWith,
toHaveBeenCalledTimes.callThrough, когда важно сохранить
оригинальную логику функции.callThrough с асинхронными тестами можно,
если функция возвращает промис или выполняет коллбэк:spyOn(api, 'fetchData').and.callThrough();
api.fetchData().then(response => {
expect(response).toBeDefined();
});
callThrough подходит только для
безопасных методов, которые можно вызывать в тестовой среде.and.callThrough является мощным инструментом Jasmine,
позволяющим контролировать и наблюдать за функцией, сохраняя её
естественное поведение, что делает тесты более реалистичными и
наглядными.