В Jasmine механизм шпионов (spies) позволяет
отслеживать вызовы функций, проверять передаваемые аргументы и
контролировать поведение тестируемого кода. Одним из мощных инструментов
является метод and.callFake, который
позволяет заменить оригинальную реализацию функции на собственную,
кастомную.
Шпион создаётся с помощью функции spyOn, которая
принимает объект и имя метода:
let calculator = {
add: function(a, b) {
return a + b;
}
};
spyOn(calculator, 'add');
После такой подстановки метод calculator.add перестает
выполнять оригинальную логику, если явно не указано иное. Чтобы заменить
его поведение на кастомное, используется and.callFake.
calculator.add.and.callFake(function(a, b) {
return a * b; // новая логика: умножение вместо сложения
});
console.log(calculator.add(2, 3)); // 6
Ключевой момент: and.callFake полностью
переопределяет оригинальную функцию на время теста.
Подмена через and.callFake позволяет создавать
динамическое поведение, зависящее от аргументов:
let api = {
fetchData: function(id) {
return { id: id, value: 'original' };
}
};
spyOn(api, 'fetchData').and.callFake(function(id) {
if (id === 1) {
return { id: id, value: 'mocked for 1' };
}
return { id: id, value: 'default mock' };
});
console.log(api.fetchData(1)); // { id: 1, value: 'mocked for 1' }
console.log(api.fetchData(2)); // { id: 2, value: 'default mock' }
Это особенно удобно для тестирования функций с различными сценариями без необходимости создавать отдельные объекты или массивы с заранее подготовленными значениями.
expect и проверкой вызововДаже при замене функции на кастомную, шпион продолжает сохранять информацию о вызовах:
spyOn(calculator, 'add').and.callFake((a, b) => a - b);
calculator.add(10, 3);
expect(calculator.add).toHaveBeenCalled();
expect(calculator.add).toHaveBeenCalledWith(10, 3);
expect(calculator.add(5, 2)).toBe(3);
Таким образом, and.callFake сочетает две возможности:
управляемое поведение функции и сохранение статистики вызовов для
ассертов.
Для функций, возвращающих промисы, and.callFake
позволяет эмулировать асинхронное поведение без реальных сетевых
запросов:
let api = {
fetchUser: function(id) {
return fetch(`/users/${id}`).then(res => res.json());
}
};
spyOn(api, 'fetchUser').and.callFake(function(id) {
return Promise.resolve({ id: id, name: 'Test User' });
});
api.fetchUser(5).then(user => {
console.log(user.name); // Test User
});
Такой подход значительно ускоряет тестирование и исключает зависимость от внешних сервисов.
and.callFake заменяет функцию
полностью, поэтому оригинальная реализация не будет вызвана,
если не использовать дополнительный механизм, например
and.callThrough().and.returnValue для простых возвращаемых значений.
callFake предпочтительнее, когда требуется логика,
зависящая от аргументов или сложное поведение.this). Внутри
функции, переданной в callFake, this будет
ссылаться на объект, на котором был вызван метод.let userService = {
currentUser: { id: 1, name: 'Alice' },
getUserName: function(prefix) {
return `${prefix}: ${this.currentUser.name}`;
}
};
spyOn(userService, 'getUserName').and.callFake(function(prefix) {
if (this.currentUser.id === 1) {
return `${prefix}: mocked ${this.currentUser.name}`;
}
return 'unknown';
});
console.log(userService.getUserName('Hello')); // Hello: mocked Alice
Этот пример демонстрирует, как можно подменять методы с
учетом состояния объекта, сохраняя доступ к this
внутри кастомной реализации.
and.callFake часто используется для:
Использование and.callFake превращает шпионы Jasmine в
гибкий инструмент, позволяющий полностью контролировать поведение
методов и функции, делая тесты более изолированными,
предсказуемыми и быстрыми.