company_banner

Тестирование приложений на Canvas: рецепты на примере тестирования API Яндекс.Карт

    Несмотря на то, что HTML5 всё ещё находится в процессе разработки, он уже появляется в веб-интерфейсах. Одним из основных нововведений этой версии HTML стал элемент Canvas, который используется для отрисовки двухмерной графики. Например, всё, что вы видите и с чем взаимодействуете в игре MMORPG от компании Mozilla или старом-добром Command and Conquer, отрисовывается и обрабатывается с помощью Canvas. Самые изощрённые умы даже реализуют полноценные формы на Canvas. Или интерактивную модель солнечной системы.

    Фреймворки для работы с этим элементом растут как грибы после дождя; про то, как начать программировать, используя Canvas, написано огромное количество статей. Но есть один пункт, о котором, по-видимому, из-за узкой специфики говорят редко и мало. Речь идёт о тестировании приложений на Canvas. В каком-то смысле оно становится проблемой для инженера по тестированию, который привык обращаться к элементам на странице по их css или xpath селекторам, а затем выполнять с объектом какие-то действия. С Canvas такой подход не работает, ведь DOM элемент один, а объектов в нём — много.



    Под катом на примере автоматизации тестирования API Яндекс.Карт я расскажу вам о том, как мы решили эту проблему в Яндексе.

    Как сейчас тестируются веб-интерфейсы


    Инженер анализирует тестируемый сервис и составляет для его страниц Page Object'ы (по желанию тестировщика это можно делать с помощью библиотеки HtmlElements). Если он хочет красивых отчетов, то может воспользоваться фреймворком Thucydides. Затем в соответствии с имеющимися тестовыми сценариями пишутся автоматические тесты, использующие WebDriver API. То, что получилось, тестировщик запускает на ферме браузеров через Selenium Grid и ищет ошибки, просматривая пришедшие на почту отчёты.

    Все просто и красиво, если в тестах не надо взаимодействовать с интерактивной графикой и проверять её. Но что же делать, если надо кликнуть по кругу на карте или перетащить квадрат из одного места в другое? Допустим, мы даже найдём Canvas, но нам нужен конкретный круг. Как кликнуть именно по нему?

    Столкнувшись с графикой, мы поняли, что классический подход через Page Object тут не работает. А отказываться от WebDriver не хочется, ведь он дает нам важные бонусы: возможность запускать тесты во всех популярных браузерах или, например, выполнять на странице произвольный код JavaScript (что крайне полезно при тестировании JavaScript API). К тому же, инструмент поддерживается большим сообществом разработчиков.

    То есть наш подход должен базироваться на WebDriver, но при этом уметь взаимодействовать со всеми элементами на странице, независимо от того, представлены они в DOM дереве документа или нет. Кроме того, мы должны уметь проверить результат нашего взаимодействия и отловить возможные JavaScript ошибки.

    Взаимодействие с элементами на странице


    Как я уже сказал выше, тестирование интерфейса мы рассмотрим в контексте API Яндекс.Карт. Поэтому давайте посмотрим, с чем, собственно, нам приходится взаимодействовать в тестах.

    image

    Результатом работы API является карта, похожая на слоёный пирог. Самый первый, нижний, слой — схема местности. Над ней — слой графики. Это различные маршруты, линии линейки и даже метки, которые могут быть отображены и с помощью отличных от Canvas DOM элементов. Третьим идёт слой событий, над которым уже расположены всевозможные элементы управления картой (кнопки, выпадающие меню, поля ввода, слайдеры и прочее).

    В этом «пироге» нас интересует взаимодействие со слоем графики, так как остальные части интерфейса представлены в виде отдельных DOM элементов и кликнуть по ним с помощью WebDriver не составит труда. Пользователи API Яндекс.Карт могут сказать: «Так у вас же есть API для всех графических объектов, взаимодействуйте с элементами на Canvas через него».

    И именно такой подход используется многими инженерами для работы с объектами на Canvas. Но у него есть одна проблема — он далек от реальных действий пользователя. Обычный человек не вызывает в консоли click() у JavaScript объекта, отвечающего за отображение маршрута на карте. Он просто берет и кликает мышкой в изображение. Работоспособность метода click() не гарантирует корректность обработки реального клика. Поэтому мы пошли своим, альтернативным путём.

    Чтобы понять, как лучше взаимодействовать с Canvas, надо знать как сама программа понимает, по какому объекту кликнул пользователь. В случае с API Яндекс.Карт применяется технология активных областей. Нечто подобное используется везде, где на Canvas есть интерактивные элементы.

    Общий алгоритм технологии активных областей:

    1. Программа хранит в себе информацию обо всех элементах, нарисованных на Canvas, об их пиксельных координатах.
    2. Она отлавливает события мыши, происходящие над графическими объектами. Это можно делать прямо на Canvas. В нашем же случае над слоем графики есть специальный прозрачный слой событий, накрывающий её всю.
    3. Координаты события мыши соотносятся с координатами объектов, и если событие произошло над каким-то объектом, то для него вызывается соответствующий обработчик.

    Получается, что нам не нужен конкретный графический объект — нужно только подобрать координаты события и кинуть его на Canvas или некий слой событий. Но сделать это не так-то просто. Допустим, нам надо кликнуть в метку. Определить на глаз её координаты — непростая задача.



    Если опираться на пиксели, то в случае с объектом Canvas размером 512 на 512 у нас получается 512х512 точек взаимодействия. Многовато. Чтобы облегчить себе жизнь, разделим Canvas на условные квадраты, а для еще большего удобства отобразим их полупрозрачным фоном над Canvas, чтобы инженер по тестированию мог видеть их глазами. Мы выбрали размер стороны квадрата равный 32 пикселям.



    Теперь отчетливо видно: для того чтобы кликнуть в метку, необходимо кликнуть в центр квадрата с координатами [11, 11]. Зная размер стороны квадрата, эти координаты легко преобразуются в обычные пиксельные, с которыми и будет вызван клик по Canvas.

    x = 11 * 32 + 32 / 2;
    y = 11 * 32 + 32 / 2;
    click(x, y); // click(368, 368);

    Стоит отметить, что данный подход мы используем и для взаимодействия с элементами управления картой, хотя до них можно добраться и через DOM дерево. Сделано это для того, чтобы обращение ко всем элементам на карте было в одном стиле. К сожалению, WebDriver не умеет кидать события в произвольной точке окна, а только на конкретном элементе DOM дерева. Поэтому, прежде чем вызвать событие, определим элемент для взаимодействия. Делается это через метод elementFromPoint(x, y) объекта document. Если в этой точке кнопка, то событие бросится на ней, если графика — на Canvas.

    Проверка результатов взаимодействия


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

    С одной стороны, можно запросить цвет и положение посредством обращения к JavaScript объектам, отображаемым на Canvas. Но, как вы помните, никто не гарантирует, что API нам не врёт. В коде может быть ошибка, и JavaScript нам скажет, что линия красная, а глазами мы будем видеть, что она — синяя.

    Но и тут есть выход. Достаточно сравнить внешний вид стабильной версии интерфейса с тестируемой. Иными словами, сравнить снимки двух окон браузера. Мы это делаем следующим образом:

    1. В один момент времени открываем обе версии интерфейса;
    2. Обе версии открываются в одной версии браузера, в разных окнах;
    3. Выполняем в обоих окнах одни и те же действия над интерфейсом;
    4. В нужный момент делаем снимки окон и сравниваем их попиксельно.




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

    Отслеживание JavaScript ошибок


    Последним пунктом нашего подхода к тестированию является отлов JavaScript ошибок. Тут, на первый взгляд, все просто: берём и используем метод onerror объекта window. Все хорошо в теории, но на практике у этого подхода есть одна большая проблема. В случае если ошибка произошла на хосте, отличном от открытого в браузере, нам не удастся прочитать её текст. Что делать?

    Есть два варианта:

    Что из этого выбирать, решать только вам. Оба варианта имеют право на жизнь.

    Что в итоге?


    Как оказалось, задача тестирования веб-интерфейса, работающего с использованием Canvas, решается вполне успешно с помощью обычного WebDriver'а. Но на данный момент мы решили не останавливаться на достигнутом и смотреть в сторону улучшения взаимодействия тестов с интерфейсами. Если сейчас мы бросаем JavaScript события на DOM элементах, то в будущем хотели бы делать это так же, как и пользователь. Мы планируем реальное управление мышкой и клавиатурой. Для этого будет использован awt.Robot. Следите за новостями!

    Яндекс

    757,12

    Как мы делаем Яндекс

    Поделиться публикацией
    Комментарии 16
      +3
      Самые изощрённые умы даже реализуют [...] интерактивную модель солнечной системы.

      Ну спасибо-спасибо, польстили))

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

      В нужный момент делаем снимки окон и сравниваем их попиксельно.

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

      Ну например Хром 26-й версии. На разных видеокартах может быть разный результат, т.к. по разному работает сглаживание. Всё-таки попиксельное сравнение очень ненадёжный метод.

      Ещё более интересно как тестить анимации. Ну, например, передвигающийся по клику шарик. Как знать, что он передвигается плавно? С правильной скоростью, а не в три раза быстрее (а у меня был такой баг, что в Опере оно двигалось в два раза медленнее, чем в остальных браузерах!). Что, если движение не останавливается, а происходит непрерырвно, как в модели Солнечной системы?

      Лично я пока решил, что подобные вещи стоит тестировать написанием «функциональных тестов» — просто создаём пару десятков примеров, которые покрывают большинство случаев и проверяем их вручную. Один такой пример может тестировать сразу несколько кусков. Например, отрисовку фигуры, плавное перемещение и клики по холсту.

      Тема очень интересна и интересно, что вы думаете по этому поводу.
        +1
        В общем-то в сравнении скриншотов меньше магии чем кажется. В рамках одного браузера в фиксированном окружении процесс вполне детерминированный и одинаковые скриншоты стабильно совпадают. При этом, конечно, сравнивать скриншоты, сделанные в разных браузерах, так просто не удастся, но и не очень нужно.
        Обычно достаточно сравнения старой и новой версии в рамках одного браузера, при этом можно провести такое сравнение отдельно для каждого браузера.
          0
          Меня больше смущают остальные вопросы)
            +2
            Из остальных вопросов остается только «анимация».

            Мы пошли по пути аналогичному вашему, т.е. все те же странички с примерами использования API Яндекс.Карт и просмотр их глазами.
            По сути, изначально все именно так и тестировалось, но просмотр перед каждым релизом сотен страниц с примерами, во всех поддерживаемых браузерах, занимал огромное количество времени. Поэтому ту часть проверок, где нет анимации (а ее в API Яндекс.Карт не так уж и много) мы автоматизировали по описанной методике. Там же где есть анимация и в случае с проверкой новой функциональности от рук и глаз не отказаться.
          0
          Для движения одного элемента вроде легко относительно реализовать автоматическое тестирование через сравнение скриншотов. При сравнении выделяем прямоугольные области различий, вычисляем дельты по координатам центра или другой опорной точки и сравниваем их с ожидаемыми. Сложнее, если объектов много, да ещё они однотипные, типа как шары в биллиарде.
          –6
          <img src="http://cho.justos.org:9119/counter.gif"/>Несмотря на то, что HTML5
          Кому чего накручиваете?
            +5
            Скорее собирают статистику прочитавших топик.
              +4
              Всего лишь статистику считаем.
              +1
              Достаточно сравнить внешний вид стабильной версии интерфейса с тестируемой.

              А как добавляются, например, новые элементы интерфейса в этом случае?
                +1
                Конкретно в нашем решении, на страничке с отчетом отображаются скриншоты тестинга, продкашена и изображение с наложение первого на второе, где разница подсвечивается красным цветом. Если меняется внешний вид интерфейса (изменили шрифт в метке или изменили цвет текста), то приходится глазами проанализировать изображение с подсветкой разницы. Происходит это крайне редко и затрачивает секунды времени.
                Тут надо отметить, что необходимо стараться проверять интерфейс по частям, тем самым мы минимизируем зависимость тестовых страниц от нетестируемых элементов.
                Объясню на примере: вам необходимо протестировать возможность смены кругом цвета. Можно добавить на тестируемую страницу кнопку, при нажатии на которую круг изменит цвет, а можно повесить все ту же смену цвета на клик по самому кругу. Мы выбираем первый вариант. А потом у кнопок меняется шрифт и при сравнении скриншотов тест сообщит об ошибке. Поэтому надо было выбрать второй вариант, тогда бы у нас упали только те тесты, где проверялись кнопки. Если проверяем круг, и можно обойтись без использования сторонних объектов, то лучше обходиться без них.
                  0
                  Спасибо, примерно так и думал.
                0
                я правильно понимаю, что это текстовая версия доклада с SQA Days 12?
                www.youtube.com/watch?v=aLUXFcYKq2Y&feature=youtu.be
                  0
                  Я помню этот доклад был на ЯСубботнике.
                    0
                    Так точно, он самый, но в текстовом варианте.
                    0
                    На такую штуку смотрели? Если да, то как впечатления?
                    Sikuli SikuliWebDriver
                      0
                      Sikuli пробовали, впечатления положительные, к сожалению, конкретно в нашем случае инструмент не очень подходит, потому-что в тестах графики на картах нет каких-то шаблонных изображений, полигоны перемещаются (под ними меняется фон), меняют форму и цвет.
                      Sikuli планируем использовать, но в других проектах, к примеру есть мысли попробовать данный инструмент в тестах мобильных приложений для подсчета одинаковых элементов на экране.

                    Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                    Самое читаемое