jest.spyOn используется для шпионажа за
существующими функциями или методами объектов без их полного
подменивания. В контексте React Testing Library этот инструмент особенно
важен при тестировании побочных эффектов, взаимодействия с внешними
модулями, браузерными API и колбэками, которые не передаются напрямую в
компонент.
В отличие от jest.fn(), который создаёт новую функцию,
jest.spyOn работает только с уже существующим
методом объекта, сохраняя его оригинальную реализацию (если
явно не переопределить поведение).
jest.spyOn(object, 'methodName')
После вызова:
Пример:
const math = {
sum(a, b) {
return a + b;
}
};
const spy = jest.spyOn(math, 'sum');
math.sum(2, 3);
expect(spy).toHaveBeenCalledWith(2, 3);
expect(spy).toHaveBeenCalledTimes(1);
Ключевая особенность jest.spyOn — возможность
наблюдать поведение без вмешательства.
jest.spyOn(console, 'log');
console.log('Hello');
expect(console.log).toHaveBeenCalledWith('Hello');
В этом примере вывод в консоль реально происходит, но одновременно фиксируется факт вызова.
mockImplementationПри необходимости оригинальное поведение можно временно заменить:
jest.spyOn(api, 'fetchUser').mockImplementation(() => {
return Promise.resolve({ name: 'Test' });
});
После этого:
Частый сценарий — подавление побочных эффектов:
jest.spyOn(window, 'alert').mockImplementation(() => {});
import { render, fireEvent } from '@testing-library/react';
import Button from './Button';
import * as analytics from './analytics';
jest.spyOn(analytics, 'trackClick');
test('отправляется событие аналитики', () => {
render(<Button />);
fireEvent.click(screen.getByRole('button'));
expect(analytics.trackClick).toHaveBeenCalled();
});
Здесь jest.spyOn позволяет проверить побочный
эффект, не вмешиваясь в сам компонент.
class UserService {
login() {
return true;
}
}
const service = new UserService();
jest.spyOn(service, 'login');
service.login();
expect(service.login).toHaveBeenCalled();
Важно: spyOn работает с конкретным
экземпляром, если метод не является статическим.
Для отслеживания вызовов методов во всех экземплярах:
jest.spyOn(UserService.prototype, 'login');
Этот приём применяется при тестировании компонентов, которые создают объекты внутри себя.
jest.spyOn корректно отслеживает асинхронные методы:
jest.spyOn(api, 'loadData').mockResolvedValue({ items: [] });
await api.loadData();
expect(api.loadData).toHaveBeenCalled();
Можно использовать:
mockResolvedValuemockRejectedValuemockImplementation(async () => ...)После тестов шпионы обязательно очищаются, иначе возможны побочные эффекты между тестами.
const spy = jest.spyOn(console, 'error');
spy.mockRestore();
afterEach(() => {
jest.restoreAllMocks();
});
mockRestore:
jest.mock| Критерий | jest.spyOn | jest.mock |
|---|---|---|
| Работает с существующей функцией | Да | Нет |
| Сохраняет реализацию по умолчанию | Да | Нет |
| Подходит для частичного контроля | Да | Нет |
| Изолирует модуль полностью | Нет | Да |
jest.spyOn применяется там, где важно проверить
факт вызова, а не заменить весь модуль.
Нельзя шпионить за:
// Нельзя
jest.spyOn(doSomething, 'call');
Метод должен существовать на момент вызова
spyOn, иначе тест упадёт с ошибкой.
describe('Component with side effect', () => {
const spy = jest.spyOn(service, 'send');
afterEach(() => {
spy.mockClear();
});
test('вызывается отправка данных', () => {
render(<Component />);
fireEvent.click(screen.getByText('Submit'));
expect(spy).toHaveBeenCalled();
});
});
Этот подход:
jest.spyOn в
философии RTLReact Testing Library ориентирован на тестирование поведения
пользователя, а не внутренней реализации компонентов.
jest.spyOn используется точечно, когда
необходимо подтвердить внешний эффект:
Инструмент не противоречит философии RTL, если применяется вне DOM-проверок и не заставляет тест зависеть от внутренней структуры компонента.