Работа с геоданными в JavaScript редко ограничивается вызовом одной функции. В реальных приложениях операции над геометрией выстраиваются в цепочки: фильтрация объектов, вычисление расстояний, построение буферных зон, пересечения слоёв, агрегация результатов. Библиотека Turf.js предоставляет набор низкоуровневых функций, каждая из которых решает локальную задачу, но ценность появляется именно при их композиции. Интеграционное тестирование в таком контексте становится ключевым инструментом проверки корректности всей геопространственной логики, а не отдельных примитивов.
Интеграционные тесты в геообработке отличаются от обычных прикладных сценариев тем, что проверяют не только результат, но и согласованность геометрических преобразований на нескольких этапах.
Ключевые сложности:
Интеграционные тесты должны учитывать, что результат — это не просто число или объект, а пространственная структура с топологией.
Типичная интеграция функций Turf.js включает последовательность операций:
polygon, point,
lineString)buffer,
intersect, union)area, distance,
centroid)booleanPointInPolygon,
booleanWithin)import * as turf from "@turf/turf";
const cityBoundary = turf.polygon([
[
[30.1, 59.9],
[30.5, 59.9],
[30.5, 60.1],
[30.1, 60.1],
[30.1, 59.9]
]
]);
const schools = turf.featureCollection([
turf.point([30.2, 60.0]),
turf.point([30.4, 60.05]),
turf.point([30.6, 60.0])
]);
const bufferZone = turf.buffer(cityBoundary, 2, { units: "kilometers" });
const schoolsInZone = schools.features.filter((school) =>
turf.booleanPointInPolygon(school, bufferZone)
);
Интеграционный тест в данном случае должен проверять не только факт попадания точек в полигон, но и корректность работы буфера и его влияние на итоговую выборку.
Геопространственные тесты требуют заранее подготовленных GeoJSON-фикстур. В отличие от обычных unit-тестов, синтетические данные часто недостаточны.
Используются три типа наборов:
Применяются для базовой проверки цепочек функций.
Используются для проверки приближённого поведения к реальному миру.
Эти данные выявляют ошибки, которые не проявляются в стандартных сценариях.
Одним из ключевых аспектов интеграционного тестирования является проверка устойчивости последовательных преобразований.
import * as turf from "@turf/turf";
test("buffer + simplify сохраняет площадь в допустимой погрешности", () => {
const poly = turf.polygon([[
[0, 0],
[0, 1],
[1, 1],
[1, 0],
[0, 0]
]]);
const buffered = turf.buffer(poly, 5, { units: "kilometers" });
const simplified = turf.simplify(buffered, { tolerance: 0.01 });
const originalArea = turf.area(buffered);
const simplifiedArea = turf.area(simplified);
expect(Math.abs(originalArea - simplifiedArea)).toBeLessThan(originalArea * 0.05);
});
Здесь проверяется не точное совпадение, а допустимое отклонение. Это принципиально важно для геоалгоритмов.
Многие сценарии строятся вокруг логических операций:
const river = turf.lineString([
[30.1, 59.95],
[30.5, 60.05]
]);
const bridges = turf.featureCollection([
turf.point([30.2, 59.98]),
turf.point([30.3, 60.01])
]);
const zone = turf.buffer(river, 1, { units: "kilometers" });
const validBridges = bridges.features.filter((b) =>
turf.booleanPointInPolygon(b, zone)
);
Интеграционный тест должен проверять согласованность буферной зоны с предикатом включения. Ошибка на любом этапе приводит к ложным результатам фильтрации.
Геометрические вычисления почти всегда содержат численные ошибки. Поэтому тестирование должно опираться на допуски.
function almostEqual(a, b, epsilon = 1e-6) {
return Math.abs(a - b) < epsilon;
}
Особенно важно это при работе с:
intersectdifferenceunionЭти операции могут возвращать геометрии с незначительными вариациями координат.
Интеграционные тесты должны учитывать не только форму, но и структуру геометрии.
Типовые проверки:
Пример проверки:
const result = turf.intersect(polyA, polyB);
expect(result).not.toBeNull();
expect(result.geometry.type).toBe("Polygon");
const coords = result.geometry.coordinates[0];
expect(coords[0]).toEqual(coords[coords.length - 1]);
Реальные приложения часто работают с несколькими слоями:
const coverageArea = turf.union(regionA, regionB);
const filteredPOI = poi.features.filter((p) =>
turf.booleanPointInPolygon(p, coverageArea)
);
const routesInArea = routes.features.map((r) =>
turf.lineIntersect(r, coverageArea)
);
Интеграционные тесты в таких сценариях проверяют согласованность всех слоёв после трансформаций.
При увеличении количества объектов возникают новые классы проблем:
Тестирование включает:
console.time("large dataset");
const result = points.features.map((p) =>
turf.nearestPoint(p, referencePoints)
);
console.timeEnd("large dataset");
Одним из критических аспектов интеграционного тестирования является устойчивость к ошибочным данным.
Примеры проблем:
Тест должен проверять поведение системы:
const invalidPoly = turf.polygon([[
[0, 0],
[1, 1],
[1, 0]
]]);
expect(() => turf.area(invalidPoly)).not.toThrow();
Особое внимание требуется к:
const datelinePoly = turf.polygon([[
[179, 10],
[-179, 10],
[-179, 20],
[179, 20],
[179, 10]
]]);
const buffered = turf.buffer(datelinePoly, 100, { units: "kilometers" });
expect(buffered).toBeDefined();
Такие тесты часто выявляют ошибки, которые не проявляются в локальных сценариях.
Интеграционные тесты для Turf.js фактически повторяют саму природу библиотеки: последовательную трансформацию геометрий.
Типичная структура теста:
Чем сложнее цепочка операций, тем важнее контроль промежуточных состояний, особенно при работе с буферами, пересечениями и объединениями слоёв.