Мокирование глобальных объектов

Мокирование глобальных объектов в Jest представляет собой важный аспект тестирования, особенно при работе с кодом, который зависит от глобальных объектов или внешних библиотек. Глобальные объекты, такие как window, document, console, а также глобальные функции, такие как setTimeout или fetch, часто используются в приложениях и могут привести к непредсказуемым результатам при тестировании. В таких случаях необходимы специальные методы мокирования, чтобы изолировать тестируемый код от внешних зависимостей.

Основы мокирования в Jest

Jest предоставляет встроенные функции для мокирования, которые позволяют заменить глобальные объекты на фиктивные, контролируемые версии. Это помогает избежать побочных эффектов при запуске тестов и позволяет тестировать функциональность в изолированной среде.

Для мокирования глобальных объектов обычно используют несколько подходов:

  1. Мокирование встроенных функций — таких как setTimeout, setInterval, console.log и другие.
  2. Мокирование внешних зависимостей — например, замену fetch или API браузера.
  3. Использование jest.spyOn — для создания шпионов на существующие функции.

Мокирование глобальных объектов с использованием jest.spyOn

Для начала рассмотрим, как использовать метод jest.spyOn для мокирования глобальных объектов. Этот метод позволяет наблюдать за функциями и изменять их поведение.

test('should mock console.log', () => {
  const spy = jest.spyOn(console, 'log').mockImplementation(() => {});
  
  console.log('Hello, world!');
  
  expect(spy).toHaveBeenCalledWith('Hello, world!');
  spy.mockRestore();
});

Здесь мы создаём шпиона на console.log, который перехватывает вызовы этой функции и заменяет её поведение. После теста шпион восстанавливается с помощью метода mockRestore.

Мокирование глобальных функций, таких как setTimeout или setInterval

Одним из распространённых случаев является мокирование функций таймеров — setTimeout и setInterval. В реальной среде эти функции могут использоваться для задержек или повторяющихся действий, но для тестирования часто требуется заменить их на фиктивные реализации, чтобы избежать задержек в процессе выполнения теста.

Jest предоставляет встроенную поддержку для мокирования таймеров с помощью функции jest.useFakeTimers().

test('should mock setTimeout', () => {
  jest.useFakeTimers();
  
  const callback = jest.fn();
  
  setTimeout(callback, 1000);
  jest.advanceTimersByTime(1000);  // Пропускаем 1000 мс
  
  expect(callback).toHaveBeenCalled();
});

Здесь используется метод jest.useFakeTimers(), чтобы Jest мог управлять временем. Функция jest.advanceTimersByTime() позволяет “перематывать” время вперёд, что даёт возможность проверять поведение кода, который использует таймеры.

Мокирование внешних API (например, fetch)

Мокирование внешних API, таких как fetch, представляет собой важный аспект тестирования приложений, которые делают HTTP-запросы. Вместо того чтобы реально отправлять запросы в сеть, можно использовать мокированные версии этих API для тестирования.

Для мокирования fetch можно использовать jest.fn(), чтобы заменить его на фиктивную функцию:

global.fetch = jest.fn(() =>
  Promise.resolve({
    json: () => Promise.resolve({ data: 'test data' }),
  })
);

test('should fetch data from API', async () => {
  const response = await fetch('https://api.example.com/data');
  const data = await response.json();
  
  expect(data).toEqual({ data: 'test data' });
  expect(fetch).toHaveBeenCalledWith('https://api.example.com/data');
});

Здесь мы заменяем глобальный fetch на фиктивную функцию, которая возвращает заранее подготовленный результат. Это позволяет тестировать код, который зависит от сети, без выполнения реальных HTTP-запросов.

Мокирование объектов браузера

Некоторые тестируемые приложения могут сильно зависеть от объектов браузера, таких как window, document, localStorage и других. Для мокирования таких объектов Jest также предоставляет специальные методы.

Для мокирования объектов браузера, таких как window или document, можно использовать Object.defineProperty() или напрямую заменять глобальные объекты на моки:

test('should mock window.innerWidth', () => {
  const originalWidth = window.innerWidth;
  Object.defineProperty(window, 'innerWidth', { value: 1024 });
  
  expect(window.innerWidth).toBe(1024);
  
  // Восстановление оригинального значения
  Object.defineProperty(window, 'innerWidth', { value: originalWidth });
});

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

Мокирование событий и слушателей

Если код зависит от событий, таких как click, resize или другие, можно использовать мокирование событийных обработчиков. Вместо того чтобы генерировать реальные события, можно использовать jest.fn() для создания моков обработчиков.

test('should handle click event', () => {
  const handleClick = jest.fn();
  
  const button = document.createElement('button');
  button.addEventListener('click', handleClick);
  
  button.click();
  
  expect(handleClick).toHaveBeenCalled();
});

Здесь создаётся элемент кнопки, на который навешивается мокированная функция обработчика события. Когда кнопка кликается, проверяется, был ли вызван обработчик.

Важные моменты при мокировании

  1. Реалистичность моков: При мокировании важно, чтобы поведение моков максимально точно соответствовало реальным объектам, с которыми работает код. Например, если вы мокируете fetch, важно, чтобы возвращаемое значение соответствовало структуре ответа от настоящего API.
  2. Изолированность тестов: Мокирование позволяет изолировать тестируемый код от внешних зависимостей, что важно для достижения предсказуемых и стабильных результатов.
  3. Очистка после тестов: Не забывайте восстанавливать оригинальные значения после каждого теста. Это предотвратит влияние одного теста на другие и поможет избежать неожиданных ошибок.

Jest предоставляет гибкие инструменты для мокирования глобальных объектов, что позволяет тестировать код в условиях, приближённых к реальной работе, без зависимости от внешних факторов.