В тестировании и разработке приложений на CesiumJS ключевым становится контроль над внешними источниками данных: тайловыми изображениями, рельефом, 3D Tiles и сервисами Cesium Ion. Эти зависимости делают систему детерминированной только при условии изоляции, поэтому мокирование провайдеров превращается в базовый инструмент устойчивой архитектуры.
CesiumJS строится вокруг абстракций поставщиков данных. Основные интерфейсы:
Каждый из этих компонентов предполагает сетевую загрузку и асинхронную работу, что делает прямое тестирование без подмены источников нестабильным.
Mocking позволяет заменить реальные сервисы предсказуемыми реализациями с фиксированным поведением.
CesiumJS активно использует интерфейсную совместимость: большинство провайдеров не требуют наследования от базового класса, достаточно реализовать ожидаемые методы.
Ключевые точки подмены:
Это позволяет создавать легковесные реализации без подключения сети и без Cesium Ion.
Самый частый сценарий — замена тайлового слоя на статическое изображение или процедурную генерацию.
class MockImageryProvider {
constructor() {
this.tileWidth = 256;
this.tileHeight = 256;
this.maximumLevel = 1;
this.minimumLevel = 0;
this.tilingScheme = new Cesium.WebMercatorTilingScheme();
this.ready = true;
}
requestImage(x, y, level) {
const canvas = document.createElement("canvas");
canvas.width = 256;
canvas.height = 256;
const ctx = canvas.getContext("2d");
ctx.fillStyle = level % 2 === 0 ? "#2b2b2b" : "#3a3a3a";
ctx.fillRect(0, 0, 256, 256);
ctx.fillStyle = "#ffffff";
ctx.fillText(`L${level} X${x} Y${y}`, 20, 130);
return Promise.resolve(canvas);
}
getTileCredits() {
return [];
}
}
Такой провайдер полностью исключает сетевые задержки и позволяет стабилизировать тесты визуализации слоёв.
Рельеф является более чувствительным компонентом, поскольку влияет на позиционирование камер и высотные вычисления. Упрощённый TerrainProvider обычно возвращает нулевую высоту.
class FlatTerrainProvider {
constructor() {
this.ellipsoid = Cesium.Ellipsoid.WGS84;
this.ready = true;
this.hasWaterMask = false;
this.hasVertexNormals = false;
}
isReady() {
return true;
}
requestTileGeometry(x, y, level) {
return Promise.resolve({
buffer: new Uint8Array(),
encoding: undefined,
stride: 0,
vertexCount: 0
});
}
getLevelMaximumGeometricError(level) {
return 1.0;
}
}
На практике более корректный вариант — делегирование в Cesium.EllipsoidTerrainProvider с отключённой детализацией, но полностью моковый вариант используется в unit-тестах.
3D Tiles является наиболее сложным объектом для подмены, поскольку включает:
Полная эмуляция невозможна без значительного объёма кода, поэтому используется упрощённый заглушечный tileset.
class MockTileset {
constructor() {
this.readyPromise = Promise.resolve(true);
this._ready = true;
this.show = true;
this.modelMatrix = Cesium.Matrix4.IDENTITY;
}
update() {
// имитация кадра обновления без загрузки данных
}
traverse(callback) {
// пустой обход дерева
}
destroy() {
this._ready = false;
}
}
Такая реализация позволяет тестировать логику интеграции Viewer без реальной загрузки ассетов.
Cesium Viewer принимает провайдеры в момент создания, что делает возможной подмену без изменения внутренней логики сцены.
const viewer = new Cesium.Viewer("container", {
imageryProvider: new MockImageryProvider(),
terrainProvider: new FlatTerrainProvider(),
baseLayerPicker: false
});
Такой подход стабилизирует:
Мокирование провайдеров часто дополняется перехватом fetch-запросов, поскольку Cesium Resource использует стандартный HTTP слой.
Сценарии:
Пример с переопределением fetch:
global.fetch = (url) => {
if (url.includes("terrain")) {
return Promise.resolve({
ok: true,
arrayBuffer: () => Promise.resolve(new ArrayBuffer(0))
});
}
return Promise.reject(new Error("Unknown resource"));
};
Более контролируемый вариант — использование MSW (Mock Service Worker), позволяющего фильтровать запросы на уровне сети без изменения кода приложения.
Resource является центральной абстракцией HTTP в CesiumJS. Подмена его методов позволяет изолировать любые сетевые операции.
Cesium.Resource.fetchArrayBuffer = () => {
return Promise.resolve(new ArrayBuffer(8));
};
Cesium.Resource.fetchJson = () => {
return Promise.resolve({ mocked: true });
};
Такая стратегия особенно эффективна при тестировании DataSource и CZML.
Cesium Viewer сильно зависит от симуляции времени. Мокирование Clock позволяет фиксировать анимации и воспроизводимость кадров.
const clock = new Cesium.Clock({
startTime: Cesium.JulianDate.now(),
currentTime: Cesium.JulianDate.now(),
multiplier: 1,
shouldAnimate: true
});
clock.tick = () => {
return Cesium.JulianDate.addSeconds(
clock.currentTime,
1,
new Cesium.JulianDate()
);
};
Фиксированное время устраняет недетерминированность при тестировании динамических сцен.
Cesium Ion часто становится источником внешней зависимости. Подмена токенов и endpoint позволяет полностью отключить облачную часть:
Cesium.Ion.defaultAccessToken = "mock-token";
Cesium.IonResource._request = () => {
return Promise.resolve({
data: {},
statusCode: 200
});
};
В связке с моками tileset это полностью исключает необходимость сетевого доступа.
В зависимости от цели тестирования выделяются уровни:
Unit mocking Подмена отдельных методов (fetchImage, sampleTerrain)
Integration mocking Замена провайдеров целиком (ImageryProvider, TerrainProvider)
Scene mocking Подмена Viewer конфигурации
Network mocking Перехват HTTP слоя
Комбинация уровней позволяет балансировать между скоростью тестов и реалистичностью сцены.
Неполная имитация провайдера приводит к расхождениям:
Поэтому мок должен сохранять контракт Cesium API, а не только форму данных.
В некоторых случаях вместо заглушек применяются процедурные провайдеры:
Это позволяет сохранять визуальную структуру сцены без сетевых зависимостей и при этом избегать пустых экранов.
Cesium активно использует Promise-based pipeline, поэтому мокирование требует контроля микротасков:
await Promise.resolve(); // стабилизация кадра
Без синхронизации тесты становятся нестабильными из-за race conditions между render loop и загрузкой ресурсов.