Интеграционное тестирование

Роль интеграционного тестирования в картографических веб-приложениях

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

В приложениях на основе Leaflet ключевую роль играет DOM-рендеринг карты, асинхронная загрузка тайлов, обработка событий мыши и касаний, а также взаимодействие с внешними источниками данных (GeoJSON, WMS, кастомные API). Поэтому интеграционные тесты должны охватывать не только JavaScript-логику, но и визуально-событийную модель карты.


Архитектура интеграционных тестов картографического слоя

Интеграционные тесты для Leaflet-приложений обычно строятся вокруг следующих уровней:

1. Уровень инициализации карты

  • создание контейнера div
  • вызов L.map()
  • установка центра и масштаба
  • подключение базовых слоёв

2. Уровень слоёв

  • tile layers (XYZ)
  • WMS слои
  • vector layers (GeoJSON)
  • группы слоёв

3. Уровень объектов

  • маркеры
  • круги и полигоны
  • всплывающие окна (popups)
  • tooltips

4. Событийный уровень

  • click / dblclick
  • zoom / move / drag
  • mouseover / mouseout
  • touch events на мобильных устройствах

5. Интеграция с внешними сервисами

  • загрузка тайлов
  • запросы GeoJSON
  • авторизация API

Подготовка тестового окружения

Интеграционные тесты для Leaflet могут выполняться в двух основных средах:

Браузерная среда (рекомендуемая):

  • Cypress
  • Playwright

Headless-окружение (ограниченное):

  • Jest + jsdom

Проблема jsdom заключается в том, что он не реализует полноценный layout engine, а Leaflet активно зависит от размеров контейнера, координат пикселей и событий DOM.

Пример минимальной HTML-фикстуры:

<div id="map" style="width: 800px; height: 600px;"></div>

Инициализация карты:

import L from 'leaflet';

const map = L.map('map').setView([51.505, -0.09], 13);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  maxZoom: 19
}).addTo(map);

Проверка инициализации карты

Интеграционный тест инициализации должен проверять:

  • создание объекта карты
  • наличие контейнера
  • корректное применение центра и zoom
  • загрузку базового слоя

Пример теста (Playwright):

test('карта инициализируется корректно', async ({ page }) => {
  await page.goto('http://localhost:3000/map');

  const mapExists = await page.evaluate(() => {
    return !!window.map && window.map.getZoom() === 13;
  });

  expect(mapExists).toBe(true);
});

Особое внимание уделяется асинхронной загрузке тайлов. Даже если карта создана, тайлы могут подгружаться позже, что требует ожиданий (waitForSelector, network idle).


Тестирование слоёв и тайлов

TileLayer является одним из самых чувствительных компонентов. Он зависит от сети, кэширования и правильной генерации URL.

L.tileLayer('https://a.tile.server/{z}/{x}/{y}.png', {
  maxZoom: 18
}).addTo(map);

Интеграционные проверки:

  • корректность формирования URL
  • загрузка изображений
  • отсутствие ошибок 404/403
  • корректная подмена поддоменов (a, b, c)

Пример проверки сетевых запросов:

page.on('request', request => {
  if (request.url().includes('.png')) {
    console.log('Tile requested:', request.url());
  }
});

Маркеры, попапы и взаимодействие объектов

Маркерный слой — одна из самых часто тестируемых частей.

const marker = L.marker([51.5, -0.09]).addTo(map);
marker.bindPopup('Hello').openPopup();

Интеграционные тесты проверяют:

  • появление маркера в DOM
  • корректное открытие popup
  • позиционирование относительно координат
  • реакцию на клики

Пример теста клика по маркеру:

test('popup открывается по клику', async ({ page }) => {
  await page.goto('/map');

  await page.click('.leaflet-marker-icon');

  const popupText = await page.textContent('.leaflet-popup-content');
  expect(popupText).toContain('Hello');
});

Тестирование событий карты

Событийная модель Leaflet критична для интерактивных приложений.

Основные события:

  • click
  • zoomend
  • moveend
  • dragend

Пример:

map.on('click', (e) => {
  console.log(e.latlng);
});

Интеграционный тест:

test('обработка клика по карте', async ({ page }) => {
  await page.goto('/map');

  await page.click('#map', { position: { x: 400, y: 300 } });

  const latlng = await page.evaluate(() => window.lastClick);
  expect(latlng).toBeDefined();
});

GeoJSON и векторные данные

Работа с GeoJSON требует проверки корректности рендеринга и стилизации.

L.geoJSON(geoData, {
  style: {
    color: 'blue'
  }
}).addTo(map);

Интеграционные тесты включают:

  • корректное отображение слоёв
  • проверку количества объектов
  • проверку стилей
  • реакцию на события объектов

Пример проверки количества объектов:

const layerCount = await page.evaluate(() => {
  return Object.keys(window.vectorLayer._layers).length;
});

expect(layerCount).toBe(5);

Асинхронная загрузка данных

Многие приложения загружают данные через fetch:

fetch('/api/geojson')
  .then(res => res.json())
  .then(data => {
    L.geoJSON(data).addTo(map);
  });

Интеграционное тестирование требует:

  • мокирования API
  • проверки состояния загрузки
  • обработки ошибок сети

Пример с Playwright:

await page.route('/api/geojson', route => {
  route.fulfill({
    contentType: 'application/json',
    body: JSON.stringify({ type: 'FeatureCollection', features: [] })
  });
});

Тестирование поведения при изменении размера контейнера

Leaflet зависит от размеров контейнера для расчёта тайлов и координат. Поэтому критично проверять:

  • корректность invalidateSize()
  • реакцию на resize
  • перерасчёт координат
map.invalidateSize();
map.setView([51.505, -0.09], 13);

Интеграционный тест:

await page.evaluate(() => {
  document.getElementById('map').style.width = '400px';
  window.map.invalidateSize();
});

Проверка мобильных жестов

Leaflet поддерживает touch-события:

  • pinch zoom
  • double tap zoom
  • drag

Интеграционные тесты в мобильных профилях браузера проверяют:

  • плавность зума
  • корректность координат
  • отсутствие конфликтов scroll/drag

Пример Playwright:

await page.touchscreen.tap(200, 200);
await page.touchscreen.tap(200, 200);

Тестирование кастомных контролов

Контролы Leaflet часто реализуются как UI-компоненты:

L.control.custom = function () {
  const control = L.control({ position: 'topright' });

  control.on Add = function () {
    const div = L.DomUtil.create('div', 'custom-control');
    div.innerHTML = 'Button';
    return div;
  };

  return control;
};

Интеграционные тесты проверяют:

  • появление DOM элемента
  • обработку кликов
  • взаимодействие с картой

Изоляция внешних зависимостей

Основная сложность интеграционного тестирования карт — внешние сервисы:

  • tile servers
  • geocoding APIs
  • feature services

Подходы:

1. Мокирование HTTP

  • route interception (Playwright)
  • sinon/fetch-mock

2. Локальный tile server

  • поднятие mock-server

3. Snapshot сетевых запросов

  • фиксация URL шаблонов

Производительность интеграционных тестов

Карты создают значительную нагрузку:

  • DOM-операции
  • canvas rendering
  • сетевые запросы

Контроль включает:

  • ограничение числа тайлов
  • отключение анимаций
  • фиксацию zoom level

Тестирование плагинов Leaflet

Экосистема плагинов расширяет функциональность:

  • clustering
  • heatmaps
  • routing

Интеграционные тесты проверяют:

  • совместимость с базовой картой
  • отсутствие конфликтов слоёв
  • корректность событий

Пример:

const markers = L.markerClusterGroup();
markers.addLayer(L.marker([51.5, -0.09]));
map.addLayer(markers);

CI-интеграция

Интеграционные тесты картографических приложений выполняются в CI окружениях:

  • GitHub Actions
  • GitLab CI
  • Jenkins

Особенности:

  • headless Chromium
  • фиксированные viewport размеры
  • отключение GPU ускорения

Пример конфигурации:

- name: Run e2e tests
  run: npx playwright test

Типичные ошибки интеграционного тестирования карт

  • отсутствие фиксации размера контейнера
  • нестабильные тайловые серверы
  • race conditions при загрузке GeoJSON
  • неверная эмуляция событий клика
  • несинхронизированные анимации zoom

Стратегии стабилизации тестов

  • использование фиксированных координат
  • ожидание load и tileload
  • отключение transition-анимаций
  • контроль сетевых задержек
  • использование deterministic fixtures

Диагностика проблем рендеринга

В интеграционных тестах важно анализировать:

  • наличие canvas элементов
  • корректность z-index слоёв
  • позиционирование popups
  • перекрытие DOM элементов
const popupVisible = await page.isVisible('.leaflet-popup');

Модель полного интеграционного сценария

Типовой сценарий включает:

  • загрузку страницы
  • инициализацию карты
  • загрузку слоёв
  • взаимодействие пользователя
  • проверку состояния UI
  • проверку сетевых запросов
  • проверку DOM изменений

Работа с временными задержками

Карты часто используют таймеры:

  • debounce перемещения
  • throttling zoom
  • delayed tile loading

Интеграционные тесты требуют контроля времени:

  • fake timers
  • ожидание событий moveend, zoomend

Взаимодействие нескольких слоёв

Сложные приложения комбинируют:

  • tile layers
  • vector layers
  • overlays

Тестируется:

  • порядок отрисовки
  • переключение visibility
  • взаимодействие событий между слоями

Проверка состояния карты после навигации

При изменении маршрута SPA:

  • карта должна сохранять состояние
  • или корректно пересоздаваться
  • не дублировать слои

Резюме архитектурных принципов тестирования

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