TDD for Responsive Design. Или как автоматизировать тестирование отображения сайта для разных устройств с помощью Galen Framework

от автора

Трудно одним заголовком сформулировать, чем же является Galen Framework. Все началось с того, что у меня возникла потребность тестировать сайты в различных браузерах и проверять: не поехала ли разметка, например, в том же Internet Explore или Chrome. Затем возникла мода на Responsive Web-Design, и пришлось вручную менять ширину браузера и проверять, как отображаются сайты. И, хотя все это время были WebDriver и Selenium Grid под рукой, так и не получалось нормально тестировать верстку сайта в Java коде. Одна из идей была: делать скриншоты в разных браузерах в Selenium Grid и затем собирать их все в один большой отчет, по которому один из тестировщиков обязан пробежаться глазами и, в случае обнаружения несоответствий, рапортовать о дефекте. К сожалению, вся эта затея долго не продержалась. Тестировщикам стало лень листать огромный отчет и сравнивать скриншоты, и они все равно пропускали мелкие дефекты. А затем пошли требования внедрения во всех сайтах Responsive Design. И вот тут появился Galen Framework. Решение оказалось простым: проверять размер и расположение элементов относительно друг друга. Для этого понадобился специальный язык Galen Specs, который было бы легко читать и понимать.

Если коротко, Galen Framework — это специальный язык и инструмент для тестирования отображения сайта в браузере. Он позволяет тестировать адаптивный дизайн, а также проводить кросс-браузерное тестирование сайта.

Galen Framework (или просто Galen) использует Selenium для открытия странички в нужном браузере, изменения размера окна браузера и получения информации об элементах на веб-страничке (абсолютное положение и размер).

В качестве еще одной идеи Galen предлагает разрабатывать фронтенд с подходом TDD (Test Driven Development). Все просто: сначала пишите тест, используя синтаксис Galen, затем реализуете фронтенд, запускаете тесты и делаете рефакторинг. Вам не обязательно следовать этой методологии, пока это просто концепт. К сожалению, Galen Framework еще не был тщательно проверен в бою, и сейчас лишь только начинаются попытки внедрения его в нашей компании. Однако, я думаю, будет интересным взглянуть на саму идею разработки фронтенда с подходом TDD, используя Galen тесты. Итак, приступим. Представим, что мы обдумываем, каким же образом нам оптимизировать сайт под разные устройства.

Шаг 1. Сделаем заготовку сайта в обычном блокноте

Попытаемся сделать простой набросок для трех разных устройств:

Как видно по рисунку, мы определили 3 типа устройств: mobile, tablet, desktop. И, судя по нашему наброску, некоторые элементы сайта будут отображаться по-разному на разных устройствах.

Шаг 2. Пишем Galen Specs

Для простоты мы все будем делать в одном файле: homepage.spec
Определим имена всех элементов на страничке, с которыми мы будем работать. Синтаксис Galen я объясню позже, а сейчас постараюсь использовать максимально упрощенные конструкции тестов. В следующих постах, если хабрапользователи посчитают этот инструмент интересным, я постараюсь изложить более сложный подход к тестированию. Также вы можете посмотреть весь синтаксис и примеры на официальном сайте galenframework.com

Так как я заранее примерно представляю, каким образом у меня будет строится разметка в html, то я могу указать «локаторы» для тестовых элементов. Вообще, в Galen можно использовать три типа локаторов: id, css, xpath. Советую избегать xpath, использовать его только в редких случаях, когда id и css не позволяют выбрать нужный элемент со страницы.

=========================================== header               id   header header-logo          css  #header .logo header-caption       css  #header h1  menu                 id   menu menu-item-*          css  #menu li a  content              id   content side-panel           id   side-panel side-panel-caption   css  #side-panel h2 side-panel-links-*   css  #side-panel li a  article              id   article article-caption      css  #article h1 article-text         id   article-text  comments             id   comments  footer               id   footer ========================================== 

Как видите, некоторые объекты имеют * в своем имени. Это нужно тогда, когда на странице много похожих элементов. Galen в таком случае сначала отыщет все элементы по заданому локатору, а затем создаст объекты с указанным именем, только вместо * он подставит порядковый номер, начиная с 1. Возьмем для примера menu-item-*. Если на нашем сайте будет 4 элемента меню, то Galen создаст соответственно объекты: menu-item-1, menu-item-2, menu-item-3, menu-item-4.

Советую пока не писать тесты для элементов меню. Лучше начать с простого: тестирование каркаса нашего сайта. Попробуем объяснить Galen, каким образом он должен тестировать наши объекты, в зависимости от размера браузера. Будем использовать четыре тега: mobile, tablet, desktop, all. Последний мы введем для собственного удобства, чтобы не пришлось перечислять все устройства для обычных тестов.

Следующий кусок кода мы добавим в наш файл homepage.spec после объявления объектов, хотя, на самом деле, не важно, в каком месте мы пишем сами проверки.

@ all ------------------------------------ header     width: 100% of screen/width     height: 100px     above: menu 0px  menu     width: 100% of screen/width     below: header 0px     above: content 0px  footer     width: 100% of screen/width     height: > 100px  content     inside: screen 0px left     below: menu 0px   @ desktop, tablet ----------------------------------- side-panel     width: 300px     below: menu 0px     inside: screen 0px right     near: content 0px right  menu     height: 50px  footer     below: content 0px   @ mobile ----------------------------------- side-panel     width: 100% of screen/width     below: content 5px  content     width: 100% of screen/width     above: side-panel 5px  footer     below: side-panel 0px 

Как видите, для desktop и tablet тегов объект content — единственный, у которого не определена ширина. Это потому, что в Galen пока еще нет возможности употреблять арифметические операции (например, 100% of screen/width — 300px). Но, с другой стороны, это бы являлось излишним. В нашем тесте есть проверка side-panel на то, что она располагается рядом с объектом content с левой стороны. Также указано, что content прикреплен к левой стороне экрана, а side-panel — к правой. Таким образом, если ширина content будет слишком маленькой или слишком большой, это отобразится в нарушении при проверке «near: content 0px right», и мы в любом случае не пропустим этот дефект.

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

Показать оптимизированный код

Мы можем убрать излишние проверки above и below. Если мы проверяем, что menu располагается над main на расстоянии 10 пикселей, то уже нет смысла проверять, что main располагается под menu на том же расстоянии. Также мы можем объединить все объекты, которые должны растягиваться по ширине экрана в одну проверку.

@ all ------------------------------------  header, menu, footer     width: 100% of screen/width  header     height: 100px     above: menu 0px  menu     height: 50px     above: content 0px  footer     height: > 100px  content     inside: screen 0px left    @ desktop, tablet -----------------------------------  side-panel     width: 300px     below: menu 0px     inside: screen 0px right     near: content 10px right  @ mobile ----------------------------------- side-panel, content     width: 100% of screen/width  side-panel     below: content 5px  

Шаг 3. Пишем Html/CSS

Давайте теперь напишем нашу главную страничку. Тут я набросал простейшую страничку, на которой можно будет протестировать наши проверки samples.galenframework.com/tutorial1/tutorial1.html

Шаг 4. Запускаем тесты в Galen

Наша главная страничка готова. Мы можем запустить тесты в galen для разных устройств. По умолчанию будет использоваться Firefox браузер.

Внимание! Т.к. Galen использует Selenium, то высока вероятность, что последний Firefox браузер не будет поддерживаться. Это одна из основных головных болей в мире Selenium, и я пока не придумал, как ее решить для Galen. Все, на что я способен — обновлять версию Selenium в Galen до последней и надеяться, что со следующим выходом Firefox ребята из Mozilla перестанут вносить серьезные изменения в API и ломать зависимость селениума. Как вариант, можно было бы разработать свой браузер, основанный на WebKit, специально для Galen, но мне кажется, что это будет пустая трата времени.

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

galen check homepage.spec --url "http://samples.galenframework.com/tutorial1/tutorial1.html" --size 1024x768 --include "all,desktop" --htmlreport desktop-reports  galen check homepage.spec --url "http://samples.galenframework.com/tutorial1/tutorial1.html" --size 600x800 --include "all,tablet"   --htmlreport tablet-reports  galen check homepage.spec --url "http://samples.galenframework.com/tutorial1/tutorial1.html" --size 400x600 --include "all,mobile"   --htmlreport mobile-reports 

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

Если нажать на подсвеченную красным проверку, то получим скриншот с выделенными блоками, которые нарушили эту самую проверку.

Как видите, в сообщении говорится «side-panel» is 0px below «content» instead of 5px. Т.е. в нашей версии сайта нет указаного отступа в 5 пикселей между объектами «content» и «side-panel» для мобильной версии. Надо что-то с этим делать. Если это баг, то будем чинить нашу верстку и добавлять этот отступ. Если же нам все равно какого размера отступ, то мы можем указать, что он должен быть в пределах разумного таким образом: below: content 0 to 10 px. Т.е. отступ может быть любой в диапазоне от 0 до 10.

После того, как мы подправим нашу верстку или исправим проверки, можно еще раз запустить тесты. Только способ запуска какой-то не очень удобный. Приходится запускать разные команды отдельно и читать отчеты в разных папках. Давайте воспользуемся еще одной особенностью Galen — так называемыми Galen Test Suites. Создадим файл galen-tutorial.test, напишем в нем следующее:

@@ Parameterized    | device   | size       | tags        |    | desktop  | 1024x768   | all,desktop |    | tablet   | 600x800    | all,desktop |    | mobile   | 400x600    | all,mobile  | Tutorial 1 Home page in ${device} device     http://samples.galenframework.com/tutorial1/tutorial1.html  ${size}         check homepage.spec --include "${tags}" 

Готово. Теперь мы можем запустить наши тесты и объединить все отчеты в один.

galen test galen-tutorial.test --htmlreport reports 

Получившийся отчет можете посмотреть по этой ссылке: samples.galenframework.com/tutorial1/reports/report.html

Более сложные и детальные проверки

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

@ all ----------------------------------- header-logo     inside: header 10px left     centered vertically inside: header     height: 60px   header-caption     inside: header 10 to 20px top left     near: header-logo ~ 10 px     text is: My Awesome Website! 

Как видите, появились новые проверки: «centered vertically inside» и «text is». Первая проверяет, что наш логотип расположен вертикально посередине и внутри объекта header, а вторая просто проверяет текст.
Еще вы, наверное, заметили новый символ "~", который означает, что мы хотим производить приблизительное сравнение в пределах 2 пикселей погрешности (в Galen можно настроить погрешность для приблизительных сравнений).

Теперь еще более сложная проверка. Давайте проверим, что на десктопе и планшете меню выложено горизонтально, а на мобильной версии таблицей в две колонки, как мы, собственно, и задумывали в нашем наброске в начале статьи. Постараюсь объяснить синтаксис каждой проверки в комментариях кода. Если вы помните, то вверху мы определили объекты меню следующим образом «menu-item-* css #menu li a». Т.е. Galen найдет все элементы меню, а т.к. у нас их только 4, то мы получим в итоге объекты menu-item-1, menu-item-2, menu-item-3, menu-item-4.

@ all --------------------------------------  # Проверяем, что все элементы меню находятся в самом меню и не выступают за края menu     contains: menu-item-*  # Все элементы меню должны иметь высоту 50 пикселей menu-item-*     height: 50px   @ desktop, tablet --------------------------------------  # Проверяем, что каждый элемент меню расположен слева от следующего элемента без отступа (0px) [ 1 - 3 ] menu-item-@     near: menu-item-@{+1} 0px left     aligned horizontally: menu-item-@{+1}   @ mobile -------------------------------------- # Каждый элемент меню должен быть примерно в половину ширины самого меню menu-item-*     width: 45 to 50 % of menu/width   # Проверяем, что первый столбец меню (табличное отображение) # расположен слева от второго без какого-либо отступа # И также проверяем, что оба элемента выравнены горизонтально по верхнему и нижнему краям [ 1, 3 ] menu-item-@     near: menu-item-@{+1} 0px left     aligned horizontally: menu-item-@{+1}  # Проверяем, что первая строка меню (табличное отображение) # расположена сверху над вторым без какого-либо отступа [1, 2] menu-item-@     above: menu-item-@{+2} 0px     aligned vertically: menu-item-@{+2} 

Конструкция [1 — 9] дает указание Galen параметризировать следующий объект со всеми его проверками и везде, где встречается символ "@", подставлять текущее значение параметра. Можно указать диапазон, а можно указать конкретные значения через запятую [1, 3, 6, 10]. Также, как видно в верхнем примере, мы использовали конструкцию @{+1}. Это простейшие арифметические операции, которые позволяют нам указать на следующий объект по списку.

И так можно продолжать до бесконечности. Как видите, проверить можно практически любое расположение элементов на странице. Главное, только правильно выбрать способ проверок, чтобы потом не создать себе проблем с поддержкой тестов.
Если вам интересно узнать, какие еще есть возможности в Galen Framework, можете посетить официальный сайт galenframework.com. Позже постараюсь выложить еще несколько статей (например, о том, как автоматизировать тестирование в разных браузерах используя Selenium Grid или о том, как правильно использовать компонентное тестирование).

Вкратце перечислю достоинства и недостатки (недоработки) Galen.

Что может Galen:

  • Проверять расположение блоков относительно друг друга
  • Проверять видимый текст
  • Работать с Selenium Grid
  • Запускать пользовательский javascript на тестовой странице (если, например, нужно открыть выпадающее меню и проверить его элементы)
  • Взаимодействовать с браузером через Selenium, для того, чтобы добраться до нужного места на сайте, если нельзя его вызвать через прямую ссылку
  • Компонентное тестирование. Galen может запускать отдельные файлы проверок для указанных объектов. Таким образом, можно реализовать проверки повторяющихся сложных элементов на странице (например, результаты поиска, комментарии и т.д.)
  • Делать корректировки размеров и положения объектов. Это может быть полезно в двух случаях: либо Selenium дает некорректную информацию о расположении объекта, либо нам нужны так называемые «guides» (или виртуальные объекты привязки), к которым мы будем относить другие объекты на странице
  • Блоки условий. Могут оказаться полезными, когда невозможно узнать, какой именно элемент будет отображен на странице (напр. баннеры)
  • Параметризация тестов в тест-сьютах. Можно, кстати, пойти дальше и использовать параметризацию параметризации (редкий случай, но можно, например, запустить один и тот же тест для разных размеров да еще и для разных браузеров)

Что пока еще не может Galen:

  • Проверять расположение видимого текста, а не всего блока (например h1 теги). В планах реализовать определение текста на скриншоте, но это будет работать только в Firefox (у Selenium проблемы с скриншотами в Chrome и IE)
  • Запускаться в реальных мобильных браузерах. Это проблема Selenium, т.к. он не позволяет получать информацию о расположении элементов на странице в Android браузере. Решением может стать разработка собственного инструмента, основаного на javascript (например как BusterJS или как Selenium 1.0). В таком случае, можно работать вообще с любым браузером. Либо можно написать свой собственный браузер для iOS и Android, который бы поддерживал коммуникацию с Galen. Но это будет слишком сложно для одного человека, поэтому я пока что отложу эту идею
  • Проверять цвет и сравнивать участки изображения на скриншотах. В планах разработать простейшее сравнение цветовой гаммы по гистограммам (например, color scheme is: 80 to 85 % black, ~10% white или color scheme like: image-samples/menu.png). Galen будет брать скриншот, выделять область указаного элемента и сверять его цветовую гамму. Также можно добавить простейшее сравнения изображений, однако, мне кажется это избыточным.
  • Выполнять сложные арифметические операции в параметризированых проверках. Пока можно делать только так: @{+1}, @{*2}, @{-1}, @{/10}. Но в планах сделать полноценные арифметические выражения с использованием скобок
  • Выполнять арифметические операции в обычных проверках. Например было бы неплохо сделать что-то наподобие width: 100% of screen/width - 10px - 100% of comments/width + 10% of somethingelse/width
  • Нет вменяемого установщика. На машине должна быть Java 1.6+, к сожалению я не имею опыта в создании установщиков, но в скором времени надеюсь что-нибудь придумать

Проект выложен на GitHub github.com/ishubin/galen и распространяется под лицензией Apache License, Version 2.0.

Galen поддерживается только одним человеком и нигде еще не распространен. Мы пытаемся внедрить его в нашей компании, но пока о каких-либо успехах говорить еще рано. Если вас заинтересовал проект, буду рад любым отзывам, особенно конструктивной критике т.к. проект находится в стадии бета и нуждается в тестировании. Дефекты можете направлять сюда github.com/ishubin/galen/issues. Скачать проект можно по этой ссылке galenframework.com/download/

ссылка на оригинал статьи http://habrahabr.ru/post/203506/