Мок модулей и шпионы

Урок объясняет, как заменить целый модуль моком и как шпионить за реальным методом.

jest.mock подменяет весь модуль заглушками, а jest.spyOn оборачивает существующий метод, отслеживая его вызовы.

Зачем мокать модули

Тестируемая функция часто зависит от других модулей: HTTP-клиента, базы, файловой системы. Дёргать их по-настоящему — медленно и ненадёжно. jest.mock заменяет модуль фейком, чтобы тест проверял только вашу логику.

jest.mock: подмена модуля

// userService.js использует api.js
const api = require('./api');

async function getUserName(id) {
  const user = await api.fetchUser(id);
  return user.name.toUpperCase();
}

module.exports = { getUserName };

В тесте подменяем api целиком — настоящего сетевого запроса не будет:

jest.mock('./api'); // авто-мок: все экспорты становятся jest.fn()
const api = require('./api');
const { getUserName } = require('./userService');

test('возвращает имя в верхнем регистре', async () => {
  api.fetchUser.mockResolvedValue({ name: 'аня' });

  const name = await getUserName(1);

  expect(name).toBe('АНЯ');
  expect(api.fetchUser).toHaveBeenCalledWith(1);
});

Так мы проверили логику getUserName в полной изоляции от сети.

jest.spyOn: наблюдение за методом

Иногда не нужно полностью подменять — достаточно проследить за реальным методом (и при желании временно подменить его поведение). Для этого есть spyOn:

const logger = {
  save(msg) { /* пишет в файл */ }
};

test('логирует сообщение об ошибке', () => {
  const spy = jest.spyOn(logger, 'save');

  handleError(logger, 'сбой');

  expect(spy).toHaveBeenCalledWith('сбой');
  spy.mockRestore(); // вернуть оригинал
});

Частый приём — заглушить шумный console.log в тесте: jest.spyOn(console, 'log').mockImplementation(() => {}).

Чистка моков между тестами

Чтобы счётчики вызовов не «протекали» между тестами, их сбрасывают:

afterEach(() => {
  jest.clearAllMocks(); // сбрасывает mock.calls у всех моков
});

Есть и опция clearMocks: true в конфиге Jest, делающая это автоматически.

mock против spy — кратко

ИнструментЧто делает
jest.fn()создаёт новую мок-функцию с нуля
jest.mock('mod')подменяет весь модуль заглушками
jest.spyOn(obj, 'm')оборачивает существующий метод, можно вернуть оригинал

Итог

  • jest.mock('./api') заменяет модуль авто-моками — изоляция от сети/диска.
  • Поведение мока задают mockResolvedValue/mockReturnValue.
  • jest.spyOn следит за реальным методом, mockRestore возвращает оригинал.
  • Между тестами моки чистят (clearAllMocks) ради изоляции.
Проверьте себя
1. Что делает jest.mock('./api')?
AУдаляет модуль api
BПодменяет модуль заглушками, чтобы тест не дёргал реальную зависимость
CУскоряет реальные запросы
DЗапускает api в отдельном процессе
2. Чем jest.spyOn отличается от jest.fn?
AНичем
BspyOn оборачивает существующий метод объекта и может вернуть оригинал, а fn создаёт мок с нуля
CspyOn работает только с числами
Dfn нельзя проверить матчерами
3. Зачем вызывать jest.clearAllMocks() в afterEach?
AЧтобы удалить тесты
BЧтобы сбросить записанные вызовы и не дать им протечь между тестами
CЧтобы ускорить установку
DЧтобы включить покрытие
Поддержать проект