Создание standalone spies с createSpy

Jasmine предоставляет мощный инструмент для изоляции и тестирования функций — spies. Среди различных способов создания spies особое место занимает standalone spy, который не привязан к объекту и создаётся напрямую с помощью метода createSpy. Этот подход полезен, когда необходимо отслеживать вызовы функций, не интегрированных в конкретный объект, или при тестировании callback-функций и независимых модулей.

Основы createSpy

Функция createSpy создаёт объект spy, который ведёт учёт всех вызовов, аргументов и контекста исполнения:

const mySpy = jasmine.createSpy('mySpy');
  • Имя spy ('mySpy') используется в отчетах тестов и для удобного вывода ошибок.
  • После создания spy ведёт счётчик вызовов: mySpy.calls.count() показывает, сколько раз функция была вызвана.
mySpy();
mySpy();
console.log(mySpy.calls.count()); // 2

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

Jasmine предоставляет набор методов для проверки вызовов spy:

  • toHaveBeenCalled() — проверка, был ли вызов функции;
  • toHaveBeenCalledTimes(n) — проверка числа вызовов;
  • toHaveBeenCalledWith(...args) — проверка аргументов конкретного вызова;
  • toHaveBeenCalledOnceWith(...args) — проверка одного вызова с определёнными аргументами.

Пример:

const callback = jasmine.createSpy('callback');

callback(1, 2);

expect(callback).toHaveBeenCalled();
expect(callback).toHaveBeenCalledTimes(1);
expect(callback).toHaveBeenCalledWith(1, 2);

Подмена реализации функции

По умолчанию standalone spy ничего не делает, кроме учёта вызовов. Для тестов часто необходимо задать конкретное поведение. Для этого используется and.callFake, and.returnValue, and.callThrough и and.throwError:

const spyWithReturn = jasmine.createSpy('spyWithReturn').and.returnValue(42);

console.log(spyWithReturn()); // 42

const spyWithFake = jasmine.createSpy('spyWithFake').and.callFake((a, b) => a + b);

console.log(spyWithFake(3, 4)); // 7
  • and.returnValue(value) — spy всегда возвращает указанное значение;
  • and.callFake(fn) — задаёт пользовательскую реализацию;
  • and.throwError(message) — вызывает ошибку при каждом вызове;
  • and.callThrough() — вызывает оригинальную функцию (актуально для метода объекта, но не standalone spy).

Проверка аргументов вызова

Методы calls.argsFor(index) и calls.allArgs() позволяют более детально исследовать вызовы:

const spy = jasmine.createSpy('spy');

spy(10, 20);
spy(30, 40);

console.log(spy.calls.argsFor(0)); // [10, 20]
console.log(spy.calls.allArgs()); // [[10, 20], [30, 40]]

Также можно проверить контекст вызова с помощью calls.all() и calls.mostRecent():

const spy = jasmine.createSpy('spy');
const obj = { method: spy };

obj.method(5);

console.log(spy.calls.mostRecent().object === obj); // true

Использование в callback и промисах

Standalone spies идеально подходят для тестирования callback-функций и асинхронного кода:

function fetchData(callback) {
    setTimeout(() => callback('data'), 100);
}

const callbackSpy = jasmine.createSpy('callbackSpy');

fetchData(callbackSpy);

setTimeout(() => {
    expect(callbackSpy).toHaveBeenCalledWith('data');
}, 150);

Для промисов можно комбинировать spies с async/await:

const asyncSpy = jasmine.createSpy('asyncSpy').and.callFake(async () => 'ok');

async function test() {
    const result = await asyncSpy();
    expect(result).toBe('ok');
    expect(asyncSpy).toHaveBeenCalled();
}

test();

Комбинация с beforeEach и очистка spy

Часто spy создаётся в beforeEach, чтобы каждый тест начинался с чистого состояния:

let mySpy;

beforeEach(() => {
    mySpy = jasmine.createSpy('mySpy');
});

it('должен вызываться один раз', () => {
    mySpy();
    expect(mySpy.calls.count()).toBe(1);
});

Это предотвращает накопление данных о вызовах между тестами и обеспечивает независимость каждого кейса.

Заключение

Standalone spies через createSpy предоставляют универсальный способ отслеживания вызовов функций, проверки аргументов, настройки возвращаемых значений и подмены поведения. Они эффективны для тестирования callback-ов, промисов, независимых модулей и любых функций, не интегрированных в объект. Комбинируя методы and.* и calls.*, можно создавать детальные и точные проверки поведения кода, что делает Jasmine гибким инструментом для юнит-тестирования.