Наша команда работает над «лицом» 2gis.ru — WebGL-библиотекой для визуализации картографических данных. В статье кратенько расскажем про WebGL (это будет важно для понимания особенностей его тестирования), про общий подход к тестированию и непосредственно про особенности тестирования приложения на WebGL [с которыми нам пришлось столкнуться].

Что вообще за WebGL

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

Довольно быстро стало понятно, что писать код для каждой конкретной видеокарты ужасно неудобно и нужен какой-то общий программный интерфейс. Так появился OpenGL (а также Metal, Vulkan, Direct3D), а потом и его реализация под веб — WebGL, которая уже лет десять как поддерживается всеми браузерами.

Чем хорош WebGL для карты

Каждый, кто хоть раз обращался к современным онлайн-картам, знает, что её (в отличие от аналоговой) можно приближать, отдалять, крутить, а ещё кликать на объекты, измерять расстояние между ними. Чтобы пользователь не возненавидел сайт в первую же минуту, всё это должно происходить незаметно для него, максимально быстро и плавно. Тут-то нам и пригождается WebGL.

Как и подобает низкоуровневому API, он умеет рисовать только очень простые двумерные фигуры (максимум — треугольники), но делает это невероятно быстро.

Тут можно посмотреть, как WebGL крутит треугольниками на GPU (если у вас слабенькое устройство, можно даже услышать, как он это делает)
Тут можно посмотреть, как WebGL крутит треугольниками на GPU (если у вас слабенькое устройство, можно даже услышать, как он это делает)

Следовательно, если мы научимся представлять все нужные нам объекты в виде [множества] треугольников, мы прямо в браузере получим возможность очень быстро рисовать и перерисовывать изображение. Чем и занимается наше приложение:

  1. Учитывает положение и размер видимой области карты; наклон, поворот и зум карты + действия пользователя.

  2. Загружает данные — что мы хотим нарисовать. 

  3. Загружает стиль — как мы хотим это нарисовать.

  4. Преобразует всё это в формат, понятный WebGL, и запускает отрисовку.

  5. [По необходимости] повторяет с пункта 1.

Успешность работы этого конвейера хотелось бы проверить.

Что нужно для тестирования

На входе есть набор переменных: источник данных для отрисовки, параметры стиля отрисовки, состояния карты или действия с картой в браузере. На выходе — нарисованный объект. Кажется, ничего сложного: комбинируем, создаём набор кейсов — готово?

Но главное — не торопиться и:

  • Понять, что будем проверять. Что является результатом работы нашего приложения? JSON с определенным содержимым? Docker-образ? Запись в базе данных? Исходя из этого можно придумать проверки и подобрать инструменты.

  • Подготовить приложение для тестирования. Как и где будет собираться актуальная версия приложения? Как тест будет получать к нему доступ, совершать действия? Без этого нечего и начинать писать автотесты.

  • Подобрать данные для тестирования. Как много данных нам нужно? Можем ли мы полностью управлять данными на своей стороне? Можем ли мы хранить их на своей стороне? Чем больше контроля и меньше вариативности, тем лучше.

  • Подобрать окружение для тестов. На каких платформах и браузерах предполагается использовать наше приложение? От каких параметров окружения зависит поведение приложения? 

И тут с нашим приложением на WebGL мы начинаем спотыкаться буквально на каждом шагу. 

Особенности тестирования приложения на WebGL

1. Локализация объектов 

Раз мы визуализируем картографические объекты, результат работы приложения — это объекты. То есть мы будем проверять объекты? Поищем в DOM-дереве и убедимся, что объект с соответствующим айдишником создался, правильно? Нет. 

Плохая новость в том, что никаких узлов в DOM под каждый объект не создаётся. Поэтому «нарисованный объект» существует в виде пикселей на экране, а в качестве узла в DOM — не существует. 

Когда в первый раз по привычке попробовал поискать объект в инспекторе

Хорошая новость в том, что проверить «нарисованность» объекта визуально мы всё-таки можем. Для этого нам нужно уметь из теста делать скриншоты, сохранять их локально в виде артефактов (для дальнейших проверок или дебага), уметь сравнивать скриншоты попиксельно.

2. Завершение отрисовки

В тестировании веб-приложений частая боль — это ожидание загрузки каких-то компонентов, изменения состояния, ответа от сервера. Нас это тоже не обошло стороной. Например, мы не можем ретраить запрос (как можно было бы сделать с RESTful API на базе HTTP) или дожидаться появления целевого объекта в DOM’е (потому что WebGL вообще не создаёт для объектов DOM-узлы, см. пункт 1). 

В итоге мы создали служебное событие, которое вызывается в приложении после завершения работы всех компонентов, влияющих на внешний вид карты. Дождавшись этого события, можно спокойно делать скриншот. 

3. Interactive behaviour

Окей, с уже визуализированными объектами разобрались, но ведь визуализация не статичная! Одно дело — проверить результат взаимодействия («кликнули по объекту — объект поменял цвет»), другое — проверить само взаимодействие. Как, например, проверить, что при клике по объекту его цвет менялся плавно в течение 10ms? Или, что при увеличении зума здания плавно вырастают?

Два из 28 видов плавности анимации на примере зданий
Два из 28 видов плавности анимации на примере зданий

Можно попробовать делать серии скриншотов и сравнивать их, но стабильность такой проверки вызывает сомнения.

4. Отсутствие окружения

Наше приложение — js-библиотека, предоставляющая API для создания в браузере карты, а не полноценный сайт (за это отвечают конечные продуктовые команды).

Наше приложение as is

Если мы собрались тестировать, как карта рисуется на странице браузера, придётся такую страницу создать! То есть нужно захостить html с подключенным кодом нашей библиотеки — по этому адресу тест сможет открыть страницу в браузере и выполнить код (создать карту уже наконец).

5. Зоопарк окружений

Отрисовка происходит на видеокарте устройства, на котором запущен браузер. Новое устройство — потенциально новые особенности отрисовки (или её отсутствие). Скриншот карты, сделанный в Chrome на Linux, может не совпадать со скриншотом на mac. Не говоря уже о том, как сложно будет дебажить локально тест, упавший в CI. 

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

6. Слишком актуальные данные 

Дефолтный источник данных, на основе которых мы рисуем почти все объекты в карте, — векторные тайлы. Это специальным образом запакованная информация об объектах на конкретном квадрате земли на определенном масштабе. Мы не должны проверять данные, их точностью и корректностью занимаются другие команды. Но мы должны проверять, что на основе одних и тех же данных приложение рисует одну и ту же картинку. Где же нам взять эти данные?  

На продакшне данные обновляются минимум раз в день: то появится какой-нибудь новый объект, то пропадёт, то подвинется, то обновится кусочек его геометрии. Все эти изменения совершенно непредсказуемы и тестировать на них с пиксельной точностью было бы сущим адом. 

Невозможно понять, обновилось начертание цифр, геометрия конкретного здания или всё сразу
Невозможно понять, обновилось начертание цифр, геометрия конкретного здания или всё сразу

Ещё можно замокать данные. Но мы рисуем на карте так много различных типов объектов, что для них потребуется создать и поддерживать собственный тайловый сервер. 

А ещё можно сделать копию определённого сегмента продакшн-данных. Но если всё-таки возникнет потребность обновить эти данные, это будет новая копия. То есть обновятся все данные, и придётся вручную актуализировать все ранее созданные скриншоты.

Что в итоге

Понятно, что без решения половины этих проблем и тестировать нечего. К сожалению, без решения других проблем можно жить довольно долго! Мы, например, спроектировали тесты без учёта кроссбраузерного запуска и гоняли полную регрессию только на одном окружении. И при переходе на WebGL 2.0 пропустили, что его поддержка в Safari до 15 версии (сентябрь 2021) была экспериментальной и включалась отдельным флагом (то есть по умолчанию фичи WebGL 2.0 просто не работали) ????. И это мы ещё не добрались до тестирования перфоманса! Про него должна быть отдельная история. 

Если хочется больше технических подробностей и примеров, вы всегда можете обратиться к webglfundamentals.org или, например, к нашему старому туториалу по основам WebGL для разработчиков. Узнать больше про WebGL для визуализации картографических данных можно тут и тут.

И если всё это вам по душе, приходите к нам! Нам очень нужны специалисты, играючи справляющиеся с техническими задачами, будем вас любить и ценить по достоинству.

Комментарии (4)


  1. Travisw
    00.00.0000 00:00

    Так можно же выводить координаты камеры и положения в пространстве и потом брать эти цифры и вставлять в программу для того чтобы получить аналогичный вид - если ошибка есть


    1. aldoha Автор
      00.00.0000 00:00

      координаты камеры и положения в пространстве и потом брать эти цифры и вставлять в программу для того чтобы получить аналогичный вид


      воспроизведение/дебаг имеется же в виду? да, в общем случае так и делаем!

      если проблема возникла [у пользователя] в конечном продукте, чаще всего после этого выясняется что проблема в данных, а не в движке карты.

      если проблема все-таки не в данных, а в нашем приложении, то оч удобно когда при одинаковых координатах, положени камеры итд у всех воспроизводится одинаково. вообще неудобно (но не менее часто случается), когда проблема стабильно воспроизводится только в конкретном окружении (у коллеги, у пользователя, в CI), а у тебя стабильно работает иначе и непонятно какой из компонентов окружения в этом виновен. например, у нас так на линуксе с включенным(!) в хроме аппаратным ускорением глитчили рекламные иконки


  1. itkuznetsov
    00.00.0000 00:00

    У сторонних пользователей API карты например Ozon Сбер и тд, когда кто то использует карту в своем продукте и у них есть скриншотные тестирования взаимодействия с картой - может возникнуть проблема с актуальностью данных на карте - как им зафризить??? карту что бы их личные скриншотные тесты были стабильны:
    1. Делать скриншоты на зумах и локациях где актуальность данных более стабильная (пригород, карта на мелких зумах где нет дорог домов)
    2. Когда скриншоты нужны там где есть дороги дома - (например нужно отметить маркеры или области из взаимодействия с картой) - можно использовать для тестирования свой кастомный стиль карты где удалить почти все или все слои стиля карты - тогда карта будет полностью белой и на ней нарисует только ваши элементы взаимодействия (маркеры полигоны и тд)