В Jasmine spy — это мощный инструмент для перехвата вызовов функций и контроля за их поведением без изменения исходного кода. Spy используется для проверки, была ли вызвана функция, с какими аргументами и сколько раз. Одним из ключевых аспектов является использование свойств spy, которые позволяют детально анализировать и управлять вызовами функций.
Существует несколько способов создания spy в Jasmine:
const calculator = {
add(a, b) {
return a + b;
}
};
spyOn(calculator, 'add');
calculator.add(2, 3);
expect(calculator.add).toHaveBeenCalled();
const mySpy = jasmine.createSpy('mySpy');
mySpy('hello');
expect(mySpy).toHaveBeenCalledWith('hello');
После создания spy, объект функции получает множество свойств для анализа.
calls — объект, содержащий информацию о всех вызовах spy.
calls.any() — возвращает true, если spy
был вызван хотя бы один раз.calls.count() — количество вызовов.calls.argsFor(index) — аргументы вызова с указанным
индексом.calls.allArgs() — массив всех аргументов для всех
вызовов.calls.all() — массив объектов вызовов с аргументами и
контекстом (this).calls.mostRecent() — объект последнего вызова.calls.first() — объект первого вызова.calls.reset() — сбрасывает всю историю вызовов
spy.Пример использования:
const obj = {
multiply(a, b) { return a * b; }
};
spyOn(obj, 'multiply');
obj.multiply(2, 3);
obj.multiply(4, 5);
expect(obj.multiply.calls.count()).toBe(2);
expect(obj.multiply.calls.argsFor(0)).toEqual([2, 3]);
expect(obj.multiply.calls.mostRecent().args).toEqual([4, 5]);
Spy позволяет не только отслеживать вызовы, но и подменять их результат:
Пример:
const service = {
fetchData() { return 'data'; }
};
spyOn(service, 'fetchData').and.returnValue('mocked data');
expect(service.fetchData()).toBe('mocked data');
spyOn(service, 'fetchData').and.callThrough();
expect(service.fetchData()).toBe('data');
Jasmine предоставляет ряд удобных матчеров для проверки spy:
toHaveBeenCalled() — был ли вызов хотя бы один
раз.toHaveBeenCalledTimes(n) — проверка точного числа
вызовов.toHaveBeenCalledWith(...args) — проверка вызова с
конкретными аргументами.toHaveBeenCalledOnceWith(...args) — был ли вызов один
раз с указанными аргументами.toHaveBeenCalledBefore(otherSpy) — проверка порядка
вызовов.toHaveBeenCalledAfter(otherSpy) — обратный порядок
вызовов.Пример:
const logger = {
log(msg) { console.log(msg); }
};
spyOn(logger, 'log');
logger.log('first');
logger.log('second');
expect(logger.log).toHaveBeenCalledTimes(2);
expect(logger.log).toHaveBeenCalledWith('first');
expect(logger.log.calls.first().args[0]).toBe('first');
expect(logger.log.calls.mostRecent().args[0]).toBe('second');
Spy сохраняет контекст вызова (this), что важно при
тестировании методов объектов:
const user = {
name: 'Alice',
greet() { return `Hello, ${this.name}`; }
};
spyOn(user, 'greet').and.callThrough();
expect(user.greet()).toBe('Hello, Alice');
expect(user.greet.calls.mostRecent().object).toBe(user);
Свойство object внутри calls показывает
объект, через который был вызван spy.
Историю вызовов можно сбросить, чтобы повторно использовать spy без влияния предыдущих вызовов:
const fn = jasmine.createSpy('fn');
fn(1);
fn.calls.reset();
expect(fn.calls.any()).toBe(false);
Spy особенно полезны при тестировании асинхронного кода, колбэков, событий и зависимостей. Например, при тестировании HTTP-запросов можно заменить реальный сервис spy, возвращающий моковые данные, и проверять взаимодействие с ним:
const api = {
getUser(id) { return fetch(`/user/${id}`); }
};
spyOn(api, 'getUser').and.callFake(id => Promise.resolve({ id, name: 'Test' }));
api.getUser(1).then(user => {
expect(user.name).toBe('Test');
});
Использование spy properties позволяет полностью контролировать тестируемые функции: фиксировать вызовы, аргументы, контекст, изменять поведение и анализировать последовательность взаимодействий между компонентами.