При разработке картографических приложений значительная часть логики завязана на источники пространственных данных: векторные слои, тайловые серверы, WMS/WMTS сервисы, внешние API. В реальных условиях такие источники могут быть нестабильными, медленными или недоступными, что делает разработку и тестирование затруднительными.
Мокирование источников данных в OpenLayers позволяет заменить реальные сервисы контролируемыми локальными или программно сгенерированными данными. Это обеспечивает предсказуемость поведения карты, ускоряет разработку и упрощает тестирование логики отображения и взаимодействия.
В OpenLayers почти любой источник данных абстрагирован через класс
ol/source/*. Это делает возможными несколько стратегий
мокирования:
VectorSource локальными GeoJSON-даннымиloader функцийКаждый подход выбирается в зависимости от уровня изоляции и целей тестирования.
Наиболее простой вариант — использование локального GeoJSON без сетевых запросов.
import VectorSource from 'ol/source/Vector';
import GeoJSON from 'ol/format/GeoJSON';
const mockGeojson = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [30.3141, 59.9386]
},
properties: {
id: 1,
name: 'Mock Point A'
}
},
{
type: 'Feature',
geometry: {
type: 'LineString',
coordinates: [
[30.31, 59.93],
[30.32, 59.94]
]
},
properties: {
id: 2,
name: 'Mock Line'
}
}
]
};
const source = new VectorSource({
features: new GeoJSON().readFeatures(mockGeojson, {
featureProjection: 'EPSG:3857'
})
});
Такой подход полностью исключает сетевую зависимость и подходит для UI-тестирования слоёв и стилей.
Более гибкий способ — управление загрузкой данных через
loader. Это позволяет симулировать серверные ответы.
import VectorSource from 'ol/source/Vector';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
const source = new VectorSource({
loader: function (extent, resolution, projection) {
const features = [
new Feature({
geometry: new Point([0, 0]),
name: 'Dynamic Mock Point'
}),
new Feature({
geometry: new Point([1000000, 1000000]),
name: 'Another Mock Point'
})
];
source.addFeatures(features);
},
strategy: function () {
return [[-180, -90, 180, 90]];
}
});
Ключевая особенность — возможность имитировать поведение сервера, который возвращает данные по запросу экстента.
Если источники данных используют url (например,
GeoJSON или TileWMS), удобно перехватывать
сетевые запросы.
Пример с переопределением fetch:
const originalFetch = window.fetch;
window.fetch = async function (url, options) {
if (url.includes('/api/features')) {
return new Response(JSON.stringify({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [10, 10]
},
properties: { name: 'Mocked via fetch' }
}
]
}));
}
return originalFetch(url, options);
};
Такой подход применяется в интеграционном тестировании, когда важно сохранить реальную структуру источника OpenLayers, но заменить транспортный слой.
Тайловые источники сложнее, поскольку работают с изображениями.
import XYZ from 'ol/source/XYZ';
const source = new XYZ({
tileUrlFunction: function (tileCoord) {
const [z, x, y] = tileCoord;
const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#e0e0e0';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = '#000';
ctx.fillText(`Z:${z} X:${x} Y:${y}`, 20, 130);
return canvas.toDataURL();
}
});
Этот метод полностью исключает сервер тайлов и позволяет проверять логику отображения сетки.
import TileWMS from 'ol/source/TileWMS';
const source = new TileWMS({
url: '/mock/wms',
params: {
LAYERS: 'mock_layer',
TILED: true
},
tileLoadFunction: function (imageTile, src) {
const canvas = document.createElement('canvas');
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#cfcfcf';
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = '#333';
ctx.fillText('Mock WMS Tile', 50, 120);
imageTile.getImage().src = canvas.toDataURL();
}
});
Иногда требуется полностью исключить формат GeoJSON и сетевые вызовы.
import VectorSource from 'ol/source/Vector';
import Feature from 'ol/Feature';
import Polygon from 'ol/geom/Polygon';
const polygon = new Feature({
geometry: new Polygon([[
[0, 0],
[1000000, 0],
[1000000, 1000000],
[0, 1000000],
[0, 0]
]])
});
const source = new VectorSource({
features: [polygon]
});
Такой источник работает как локальная база геометрий и используется для симуляции сложных сцен.
OpenLayers использует стратегии загрузки (strategy) для
управления запросами. Их также можно подменять для полного контроля
поведения источника.
import { bbox as bboxStrategy } from 'ol/loadingstrategy';
const source = new VectorSource({
loader: function () {
// игнорирует extent
},
strategy: function () {
return [[0, 0, 0, 0]];
}
});
Полный контроль над стратегией позволяет моделировать:
При работе с REST API, которые возвращают GeoJSON или тайлы, применяется перехват на уровне сети.
import { rest } from 'msw';
import { setupServer } from 'msw/node';
const server = setupServer(
rest.get('/api/geo', (req, res, ctx) => {
return res(
ctx.json({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [20, 20]
},
properties: { label: 'MSW Point' }
}
]
})
);
})
);
server.listen();
Этот способ сохраняет реальную интеграцию OpenLayers с источниками, но полностью контролирует данные.
Иногда важно проверять не только геометрию, но и поведение стилей, зависящих от свойств объектов.
import Style from 'ol/style/Style';
import Fill from 'ol/style/Fill';
import CircleStyle from 'ol/style/Circle';
function mockStyle(feature) {
const type = feature.get('type');
return new Style({
image: new CircleStyle({
radius: type === 'critical' ? 10 : 5,
fill: new Fill({
color: type === 'critical' ? 'red' : 'blue'
})
})
});
}
Использование мок-данных с предсказуемыми атрибутами позволяет проверять ветвление логики стилей.
Для проверки производительности и кластеризации часто требуется большое количество объектов.
import VectorSource from 'ol/source/Vector';
import Feature from 'ol/Feature';
import Point from 'ol/geom/Point';
const features = [];
for (let i = 0; i < 10000; i++) {
features.push(
new Feature({
geometry: new Point([
Math.random() * 1e6,
Math.random() * 1e6
]),
id: i
})
);
}
const source = new VectorSource({
features
});
Такой подход используется для проверки кластеризации, рендеринга и поведения карты под нагрузкой.
В реальных сценариях часто комбинируются разные типы мокирования:
Это позволяет моделировать многослойные карты без подключения к внешним сервисам, сохраняя поведение, максимально близкое к продакшну.