Наша команда работает над «лицом» 2gis.ru — WebGL-библиотекой для визуализации картографических данных. В статье кратенько расскажем про WebGL (это будет важно для понимания особенностей его тестирования), про общий подход к тестированию и непосредственно про особенности тестирования приложения на WebGL [с которыми нам пришлось столкнуться].  Примерно 30 лет назад, когда изобрели относительно мощные видеокарты, люди придумали, что часть вычислений можно выполнять непосредственно на них и не тратить время на передачу информации между основным и графическим процессором.  Довольно быстро стало понятно, что писать код для каждой конкретной видеокарты ужасно неудобно и нужен какой-то общий программный интерфейс. Так появился OpenGL (а также Metal, Vulkan, Direct3D), а потом и его реализация под веб — WebGL, которая уже лет десять как поддерживается всеми браузерами. Каждый, кто хоть раз обращался к современным онлайн-картам, знает, что её (в отличие от аналоговой) можно приближать, отдалять, крутить, а ещё кликать на объекты, измерять расстояние между ними. Чтобы пользователь не возненавидел сайт в первую же минуту, всё это должно происходить незаметно для него, максимально быстро и плавно. Тут-то нам и пригождается WebGL. Как и подобает низкоуровневому API, он умеет рисовать только очень простые двумерные фигуры (максимум — треугольники), но делает это невероятно быстро. Следовательно, если мы научимся представлять все нужные нам объекты в виде [множества] треугольников, мы прямо в браузере получим возможность очень быстро рисовать и перерисовывать изображение. Чем и занимается наше приложение:  Учитывает положение и размер видимой области карты; наклон, поворот и зум карты + действия пользователя. Загружает данные — что мы хотим нарисовать.  Загружает стиль — как мы хотим это нарисовать. Преобразует всё это в формат, понятный WebGL, и запускает отрисовку. [По необходимости] повторяет с пункта 1. Успешность работы этого конвейера хотелось бы проверить.  На входе есть набор переменных: источник данных для отрисовки, параметры стиля отрисовки, состояния карты или действия с картой в браузере. На выходе — нарисованный объект. Кажется, ничего сложного: комбинируем, создаём набор кейсов — готово?  Но главное — не торопиться и: Понять, что будем проверять. Что является результатом работы нашего приложения? JSON с определенным содержимым? Docker-образ? Запись в базе данных? Исходя из этого можно придумать проверки и подобрать инструменты. Подготовить приложение для тестирования. Как и где будет собираться актуальная версия приложения? Как тест будет получать к нему доступ, совершать действия? Без этого нечего и начинать писать автотесты. Подобрать данные для тестирования. Как много данных нам нужно? Можем ли мы полностью управлять данными на своей стороне? Можем ли мы хранить их на своей стороне? Чем больше контроля и меньше вариативности, тем лучше. Подобрать окружение для тестов. На каких платформах и браузерах предполагается использовать наше приложение? От каких параметров окружения зависит поведение приложения?  И тут с нашим приложением на WebGL мы начинаем спотыкаться буквально на каждом шагу.  Раз мы визуализируем картографические объекты, результат работы приложения — это объекты. То есть мы будем проверять объекты? Поищем в DOM-дереве и убедимся, что объект с соответствующим айдишником создался, правильно? Нет.  Плохая новость в том, что никаких узлов в DOM под каждый объект не создаётся. Поэтому «нарисованный объект» существует в виде пикселей на экране, а в качестве узла в DOM — не существует.  Хорошая новость в том, что проверить «нарисованность» объекта визуально мы всё-таки можем. Для этого нам нужно уметь из теста делать скриншоты, сохранять их локально в виде артефактов (для дальнейших проверок или дебага), уметь сравнивать скриншоты попиксельно. В тестировании веб-приложений частая боль — это ожидание загрузки каких-то компонентов, изменения состояния, ответа от сервера. Нас это тоже не обошло стороной. Например, мы не можем ретраить запрос (как можно было бы сделать с RESTful API на базе HTTP) или дожидаться появления целевого объекта в DOM’е (потому что WebGL вообще не создаёт для объектов DOM-узлы, см. пункт 1).  В итоге мы создали служебное событие, которое вызывается в приложении после завершения работы всех компонентов, влияющих на внешний вид карты. Дождавшись этого события, можно спокойно делать скриншот.  Окей, с уже визуализированными объектами разобрались, но ведь визуализация не статичная! Одно дело — проверить результат взаимодействия («кликнули по объекту — объект поменял цвет»), другое — проверить само взаимодействие. Как, например, проверить, что при клике по объекту его цвет менялся плавно в течение 10ms? Или, что при увеличении зума здания плавно вырастают? Можно попробовать делать серии скриншотов и сравнивать их, но стабильность такой проверки вызывает сомнения. Наше приложение — js-библиотека, предоставляющая API для создания в браузере карты, а не полноценный сайт (за это отвечают конечные продуктовые команды).  Если мы собрались тестировать, как карта рисуется на странице браузера, придётся такую страницу создать! То есть нужно захостить html с подключенным кодом нашей библиотеки — по этому адресу тест сможет открыть страницу в браузере и выполнить код (создать карту уже наконец). Отрисовка происходит на видеокарте устройства, на котором запущен браузер. Новое устройство — потенциально новые особенности отрисовки (или её отсутствие). Скриншот карты, сделанный в Chrome на Linux, может не совпадать со скриншотом на mac. Не говоря уже о том, как сложно будет дебажить локально тест, упавший в CI.  Можно гонять тесты в одном популярном окружении и пропустить баг в менее популярном. Или запихать тесты в docker и игнорировать все особенности операционных систем. Или гонять тесты в нескольких окружениях и поддерживать набор эталонных скриншотов, кратный количеству этих окружений. В итоге нам нужно как минимум выбрать для тестов фреймворк, позволяющий выполнять один и тот же тест в разных браузерах. А в идеале сразу смотреть в сторону инструментов типа browserstack, позволяющих запустить приложение одновременно и в разных браузерах и на разных операционных системах. Дефолтный источник данных, на основе которых мы рисуем почти все объекты в карте, — векторные тайлы. Это специальным образом запакованная информация об объектах на конкретном квадрате земли на определенном масштабе. Мы не должны проверять данные, их точностью и корректностью занимаются другие команды. Но мы должны проверять, что на основе одних и тех же данных приложение рисует одну и ту же картинку. Где же нам взять эти данные?   На продакшне данные обновляются минимум раз в день: то появится какой-нибудь новый объект, то пропадёт, то подвинется, то обновится кусочек его геометрии. Все эти изменения совершенно непредсказуемы и тестировать на них с пиксельной точностью было бы сущим адом.  Ещё можно замокать данные. Но мы рисуем на карте так много различных типов объектов, что для них потребуется создать и поддерживать собственный тайловый сервер.  А ещё можно сделать копию определённого сегмента продакшн-данных. Но если всё-таки возникнет потребность обновить эти данные, это будет новая копия. То есть обновятся все данные, и придётся вручную актуализировать все ранее созданные скриншоты. Понятно, что без решения половины этих проблем и тестировать нечего. К сожалению, без решения других проблем можно жить довольно долго! Мы, например, спроектировали тесты без учёта кроссбраузерного запуска и гоняли полную регрессию только на одном окружении. И при переходе на WebGL 2.0 пропустили, что его поддержка в Safari до 15 версии (сентябрь 2021) была экспериментальной и включалась отдельным флагом (то есть по умолчанию фичи WebGL 2.0 просто не работали) 🤡. И это мы ещё не добрались до тестирования перфоманса! Про него должна быть отдельная история.  Если хочется больше технических подробностей и примеров, вы всегда можете обратиться к webglfundamentals.org или, например, к нашему старому туториалу по основам WebGL для разработчиков. Узнать больше про WebGL для визуализации картографических данных можно тут и тут. И если всё это вам по душе, приходите к нам! Нам очень нужны специалисты, играючи справляющиеся с техническими задачами, будем вас любить и ценить по достоинству.