Jest: spyOn

jest.spyOn создаёт spy (шпиона) — обёртку над существующим методом объекта, которая отслеживает вызовы и при необходимости подменяет реализацию, сохраняя возможность восстановления оригинала.

Зачем нужно

В отличие от jest.fn, spyOn работает с уже существующими методами — позволяет следить за ними или временно заменить, не ломая весь модуль. Это ключевой инструмент для тестирования side effects: отправки аналитики, вызова console.error, записи в localStorage.

Где используется

  • Проверка, что метод был вызван с нужными аргументами
  • Подмена console.error/console.warn чтобы тест не шумел в stdout
  • Мокирование метода класса или объекта без замены всего модуля
  • Временная подмена реализации с последующим восстановлением (mockRestore)

Основной контент

Базовый spy — только слежка

import * as mathUtils from './mathUtils';

test('вызывает calculateTax при оформлении заказа', () => {
  const spy = jest.spyOn(mathUtils, 'calculateTax');

  placeOrder({ amount: 1000 });

  expect(spy).toHaveBeenCalledTimes(1);
  expect(spy).toHaveBeenCalledWith(1000);

  spy.mockRestore(); // восстанавливаем оригинал
});

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

test('обрабатывает ошибку fetch', async () => {
  const spy = jest.spyOn(global, 'fetch').mockResolvedValue({
    ok: false,
    status: 500,
    json: async  => ({ error: 'Server error' }),
  });

  await expect(getUser(1)).rejects.toThrow('Server error');

  spy.mockRestore();
});

Spy на console

test('логирует ошибку при невалидном вводе', () => {
  const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});

  validateForm({ name: '' });

  expect(consoleSpy).toHaveBeenCalledWith('Name is required');
  consoleSpy.mockRestore();
});

Spy на метод класса

import { UserService } from './UserService';

test('вызывает save при создании пользователя', async () => {
  const service = new UserService();
  const saveSpy = jest.spyOn(service, 'save').mockResolvedValue({ id: 1 });

  await service.createUser({ name: 'Alice' });

  expect(saveSpy).toHaveBeenCalledWith({ name: 'Alice' });
});

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

expect(spy).toHaveBeenCalledWith(expect.objectContaining({ id: 1 }));
expect(spy).toHaveBeenLastCalledWith('error', 'message');
expect(spy).toHaveBeenNthCalledWith(2, 'second call arg');

Восстановление в afterEach

afterEach(() => {
  jest.restoreAllMocks; // восстанавливает все spyOn
});

Частые ошибки

  • Забытый mockRestore — spy остаётся активным и ломает другие тесты; используй jest.restoreAllMocks в afterEach
  • spyOn на ES-модуль по умолчанию — не работает напрямую; нужно импортировать * as module и следить за свойством
  • mockImplementation вместо mockReturnValue — для простого возврата значения достаточно mockReturnValue; mockImplementation нужна только для сложной логики
  • Проверка toHaveBeenCalled без вызова кода — убедись, что тестируемая функция реально вызывает отслеживаемый метод

Связанные темы

Ресурсы