Автоматизация тестирования в приложениях на базе CesiumJS требует учета особенностей WebGL-рендеринга, асинхронной подгрузки тайлов, геопространственных источников и состояния камеры. В отличие от классических фронтенд-приложений, здесь критичны стабильность графического контекста, детерминизм сцены и контроль времени симуляции.
Архитектура тестов обычно делится на три уровня: модульные тесты, интеграционные и end-to-end сценарии. Каждый уровень решает собственный класс задач и использует разные стратегии изоляции среды выполнения.
Модульное тестирование применяется к бизнес-логике, не завязанной напрямую на WebGL. Это могут быть утилиты преобразования координат, расчеты траекторий, обработка данных тайлов или конфигурации сцен.
Чаще всего используется Jest, благодаря встроенной системе моков и возможности запускать тесты в Node.js среде без браузера.
Типичный подход — максимальное отделение логики от объектов
Viewer и Scene.
// функция без зависимости от Cesium Viewer
export function normalizeHeight(height, ellipsoidRadius) {
return height / ellipsoidRadius;
}
import { normalizeHeight } from "./math";
test("normalizes height correctly", () => {
expect(normalizeHeight(10, 2)).toBe(5);
});
Ключевой принцип — избегание прямых вызовов CesiumJS API
внутри тестируемых функций. Если зависимость неизбежна, применяется
мокирование.
Объекты Viewer, Scene, Camera,
ImageryLayer обладают тяжелой инициализацией и требуют
WebGL-контекста. Поэтому в модульных тестах они заменяются
заглушками.
const mockScene = {
requestRender: jest.fn(),
primitives: {
add: jest.fn(),
},
};
const mockViewer = {
scene: mockScene,
camera: {
setView: jest.fn(),
},
};
Подход позволяет тестировать взаимодействия, не поднимая реальный рендеринг.
Особое внимание уделяется API, связанному с тайлами и источниками
данных. Например, ImageryProvider часто заменяется
фиктивным провайдером, возвращающим предсказуемые данные.
Интеграционный уровень требует запуска браузерного окружения. Здесь уже создается реальный экземпляр CesiumJS, но с ограниченным набором ресурсов.
Используются headless-браузеры, чаще всего в связке с Playwright или Cypress.
Основная задача — проверка корректности взаимодействия компонентов: камеры, сцены, сущностей и данных.
import { test, expect } from "@playwright/test";
test("camera moves to target position", async ({ page }) => {
await page.goto("http://localhost:3000");
await page.evaluate(() => {
viewer.camera.flyTo({
destination: Cesium.Cartesian3.fromDegrees(30, 50, 1000),
});
});
const position = await page.evaluate(() =>
viewer.camera.positionWC.clone()
);
expect(position).toBeDefined();
});
Ключевая сложность — нестабильность визуального состояния. Даже небольшие различия в таймингах приводят к разным результатам, поэтому проверки строятся на диапазонах значений, а не строгих равенствах.
E2E тестирование охватывает полный цикл: загрузка сцены, подгрузка тайлов, рендеринг объектов, взаимодействие пользователя.
Используется Selenium или Playwright в режиме полного рендеринга.
Основной акцент делается на:
Проверка визуального состояния часто выполняется через скриншотное тестирование.
await page.screenshot({ path: "scene.png" });
expect(await page.screenshot()).toMatchSnapshot("scene-baseline.png");
При работе с Cesium важно учитывать динамические элементы сцены: облака, атмосферу, тайлы, которые могут изменяться между запусками.
CesiumJS активно использует внутренние часы (Clock) для
анимации, симуляции времени и интерполяции данных.
Для тестов критично фиксировать время:
viewer.clock.currentTime = Cesium.JulianDate.fromIso8601("2025-01-01T00:00:00Z");
viewer.clock.shouldAnimate = false;
Без фиксации времени тесты становятся нестабильными из-за различий в кадрах и обновлениях сцен.
Также применяется ручное управление requestRenderMode,
чтобы исключить непрерывный рендеринг:
viewer.scene.requestRenderMode = true;
viewer.scene.requestRender();
Одной из ключевых проблем автоматизации является отсутствие полноценной поддержки WebGL в headless-режимах. Некоторые CI-системы требуют использования виртуального GPU или software renderer.
Типичные стратегии:
headless=new в Chromium--use-gl=angleПри этом поведение сцены может отличаться от реального GPU, поэтому визуальные тесты дополняются логическими проверками состояния сцены.
Работа с 3D Tiles — одна из самых сложных областей тестирования.
Основные проблемы:
Для стабилизации применяются моки Tileset и подмена
TileProvider.
const mockTileset = {
ready: true,
root: {},
tilesLoaded: true,
maximumScreenSpaceError: 16,
};
Также используется искусственная эмуляция загрузки:
await tileset.readyPromise;
tileset.update();
Snapshot-подход применяется для контроля визуальной регрессии. Однако в контексте CesiumJS он требует строгой стабилизации сцены:
Иначе снимки становятся недетерминированными.
Автоматизация тестов включается в pipeline через этапы:
При использовании контейнеризации важно обеспечить наличие графического backend:
При тестировании сложных сцен критично сохранять диагностическую информацию:
position, direction,
up)console.log(viewer.scene.debugShowFramesPerSecond);
console.log(viewer.scene.globe.tilesLoaded);
Это позволяет воспроизводить ошибки, возникающие только при определенных условиях загрузки или прокрутки сцены.
CesiumJS активно использует промисы и событийную модель:
tileset.allTilesLoadedcamera.moveEndscene.postRenderВ тестах применяется ожидание событий вместо таймаутов:
await new Promise(resolve => {
viewer.scene.postRender.addEventListener(() => resolve());
});
Такой подход снижает флаки-тесты и повышает воспроизводимость сценариев.
Сценарии взаимодействия включают:
В Playwright:
await page.mouse.wheel(0, -500);
await page.mouse.click(300, 300);
Проверяется изменение состояния камеры и корректность выбранных объектов.
Автоматизированные тесты часто дополняются измерениями производительности:
const start = performance.now();
await viewer.scene.render();
const duration = performance.now() - start;
Это позволяет отслеживать деградацию рендеринга при изменениях кода.