Мокирование browser API

Тестирование JavaScript-кода, взаимодействующего с браузерными API, требует точного контроля среды выполнения. В реальных условиях браузер предоставляет множество глобальных объектов и функций — window, document, fetch, localStorage, sessionStorage, navigator, alert и др. При запуске тестов через Karma необходимо изолировать их от реальных браузерных эффектов, чтобы гарантировать воспроизводимость и независимость результатов. Для этого используется мокирование (mocking) — замена реальных функций и объектов их тестовыми аналогами.


Глобальные объекты и функции

В браузере глобальная область видимости представлена объектом window. Часто требуется мокировать:

  • window.alert, window.confirm, window.prompt Эти методы напрямую влияют на пользовательский интерфейс. Для тестов создаются заглушки, которые фиксируют вызовы, но не блокируют выполнение кода.
beforeEach(() => {
  spyOn(window, 'alert'); // Перехватывает вызовы alert
});

it('вызывает alert с правильным сообщением', () => {
  someFunctionThatCallsAlert();
  expect(window.alert).toHaveBeenCalledWith('Привет');
});
  • localStorage и sessionStorage Для тестов создаются объекты-заглушки, имитирующие методы getItem, setItem, removeItem, clear.
beforeEach(() => {
  let store = {};
  spyOn(window.localStorage, 'getItem').and.callFake(key => store[key] || null);
  spyOn(window.localStorage, 'setItem').and.callFake((key, value) => { store[key] = value; });
  spyOn(window.localStorage, 'removeItem').and.callFake(key => { delete store[key]; });
  spyOn(window.localStorage, 'clear').and.callFake(() => { store = {}; });
});

Мокирование сетевых запросов

Для тестирования функций, использующих fetch или XMLHttpRequest, создаются поддельные реализации:

  • fetch
beforeEach(() => {
  spyOn(window, 'fetch').and.callFake((url, options) => {
    return Promise.resolve({
      ok: true,
      json: () => Promise.resolve({ data: 'mocked' })
    });
  });
});

it('обрабатывает ответ fetch', async () => {
  const result = await fetchData(); // Внутри fetchData вызывается window.fetch
  expect(result.data).toBe('mocked');
});
  • XMLHttpRequest Karma запускается в реальном браузере, но для юнит-тестов проще использовать библиотеку jasmine-ajax:
beforeEach(() => {
  jasmine.Ajax.install();
});

afterEach(() => {
  jasmine.Ajax.uninstall();
});

it('отправляет GET-запрос и получает ответ', () => {
  fetchDataViaXHR();
  const request = jasmine.Ajax.requests.mostRecent();
  expect(request.url).toBe('/api/data');
  request.respondWith({
    status: 200,
    responseText: '{"data":"mocked"}'
  });
});

Мокирование таймеров

Функции типа setTimeout, setInterval также часто требуют контроля:

beforeEach(() => {
  jasmine.clock().install();
});

afterEach(() => {
  jasmine.clock().uninstall();
});

it('вызывает функцию через 1 секунду', () => {
  const callback = jasmine.createSpy('callback');
  setTimeout(callback, 1000);
  jasmine.clock().tick(1000);
  expect(callback).toHaveBeenCalled();
});

Использование таймеров с моками обеспечивает синхронное тестирование асинхронного кода, что особенно важно для Karma, где асинхронные процессы могут влиять на порядок выполнения тестов.


Работа с DOM

Мокирование элементов DOM позволяет тестировать взаимодействие с документом без реального отображения:

beforeEach(() => {
  const div = document.createElement('div');
  div.id = 'test-container';
  document.body.appendChild(div);
});

afterEach(() => {
  const div = document.getElementById('test-container');
  if (div) document.body.removeChild(div);
});

it('изменяет текст элемента', () => {
  const el = document.getElementById('test-container');
  el.textContent = 'Hello';
  updateElementText(el, 'World');
  expect(el.textContent).toBe('World');
});

Использование моков для DOM предотвращает побочные эффекты между тестами и ускоряет выполнение, так как не требуется рендеринг сложных компонентов.


Библиотеки для мокирования

Помимо встроенных возможностей Jasmine, Karma позволяет использовать дополнительные библиотеки:

  • Sinon.js — создание шпионов, заглушек и моков с расширенными возможностями.
  • jest-mock — может использоваться для тестов в комбинации с Karma через адаптеры.
  • jasmine-ajax — специализированное мокирование XHR-запросов.

Применение этих инструментов повышает гибкость тестов и позволяет точно контролировать поведение browser API без обращения к реальному браузеру.


Практические рекомендации

  1. Всегда изолировать тесты друг от друга, откатывая изменения глобальных объектов после каждого теста (afterEach).
  2. Использовать шпионы (spyOn) для проверки вызовов функций, вместо изменения поведения реальных методов.
  3. Для асинхронного кода применять мокирование таймеров и промисов.
  4. Минимизировать прямое взаимодействие с реальным DOM, создавая временные контейнеры.
  5. Проверять, что все моки сбрасываются после тестов, иначе возможны ложные срабатывания в других тестах.

Контролируемое мокирование browser API делает тесты надёжными, предсказуемыми и полностью изолированными от внешних факторов, обеспечивая стабильность и скорость при работе с Karma.