Тестируем CSS в Selenium IDE

css

Я все больше в своей практике пытаюсь использовать автоматизированное тестирование. Стараюсь при этом не плодить инструменты и библиотеки, обходиться простыми подходами. Не так давно, я задумался о том, как протестировать CSS-файлы. Поиск по Интернету выявил следующие точки зрения на этот вопрос:

  1. Тестирование CSS не имеет смысла, так как это декларативный язык, а его результатом является сверстанное изображение страницы, которое можно оценить лишь визуально.
  2. Протестировать CSS можно с помощью снятия битмапов с сгенерированной страницы и сверка ее с эталонным изображением. Для этого даже есть некоторые инструменты.
  3. Нашлась некоторая библиотека CSS-Unit.

Должен сказать, что все варианты мне не понравились. В конечном итоге мне удалось протестировать CSS используя текстовый редактор, Firefox + Selenium IDE и… и больше ничего.

Немного теории


Наиболее конструктивные идеи мне принесла длительная медитация над вкладкой Firebag «Cкомпилированный стиль».



По содержимому этого окна стало ясно, что браузер генерирует страницу как минимум в два этапа.

  1. Определение всех параметров всех элементов, на основе CSS
  2. Верстка страницы, на основе полученных значений

Если второй этап никак не протестировать, кроме как битмапами, то первый, скорее всего, вполне поддается автоматизированному тестированию. А ведь именно на первом этапе возникают почти все ошибки в написании CSS. Больше половины жизни WEB-верстальщик проводит, рассматривая зачеркнутые и подсвеченные строчки в отладочном окне стилей, пытаясь понять, что от чего унаследовалось, и что к чему применилось. Если удастся это протестировать, мы снимем большинство проблем с CSS-файлами в нашем проекте.

Все, что нам нужно – это получить доступ к значениям, показывающимся в закладке Firebag «Cкомпилированный стиль». Недолгое рыскание по Интернету привело к обнаружению функции:

var style = window.getComputedStyle(element[, pseudoElt]);

Название, говорит само за себя. Единственное, что можно пояснить – это второй параметр. Он может быть полезен, если мы захотим, например, узнать, какого цвета будет ссылка, если на нее наведут курсор. Осталось только вспомнить, что в Selenium IDE есть замечательное семейство функций xxxEval, которые могут вычислить любое JavaScript выражение. Теперь можно опробовать все это на практике.

Немного практики


Проводим небольшую подготовительную работу. Создаем структуру папок:

project – корень проекта 
project/css – место для css 
project/tests – место для тестовой инфраструктуры 
project/tests/html-examples – место для тестовых примеров html-кода 
project/tests/selenium-tests – место для Selenium-тестов

Отдаем папку project под управление нашему любимому http-серверу, и настраиваем к нему доступ по адресу project.localhost.

В корне, по давно сложившейся привычке, заводим файл index.html, который делает наш проект похожим на реальный, а также символизирует исходный код проекта, безжалостно изменямый программистами независимо от нашей воли и желания. Одновременно заводим файл styles.css, который содержит css-код который мы будем разрабатывать и тестировать.

Предположим, в нашем проекте нам нужно выводить уведомляющие сообщения. Они должны выводится зеленым шрифтом. Сообщение состоит из заголовка и основного текста . Шрифт заголовка 20px, основного текста 14px. Для размещения сообщений программисты зарезервировали контейнер . В соответствии с этой ситуацией наши файлы index.html и styles.css на момент начала тестирования имеют следующий вид:

project.localhost/index.html
<html>
<head> 
<link rel="stylesheet" type="text/css" href="/css/styles.css"> 
</head> 
<body>  
    <h1>Super project</h1>
    <p>It's really very big and complex project !</p> 
    <div class="notify-message"> 
       <h1>Hi,Guest</h1>
        <p>Welcome to our beautiful world!</p> 
    </div> 
</body> 
</html>


project.localhost/css/styles.css
div.notify-message h1 { 
  color:green; 
  font-size: 20px; 
} 

div.notify-message p { 
  color:green; 
  font-size: 14px; 
}


Нам нужно протестировать styles.css. Велик соблазн написать тест сразу с использованием index.html. Но мы помним, что он может меняться (а может и вообще исчезнуть при очередной смене тим-лидера). Поэтому воспроизводим требуемую ситуацию в файле notify-message-example.html

project.localhost/tests/html-examples/notify-message-example.html
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/css/styles.css">
  </head>
  <body>
    <div class="notify-message">
       <h1 id="h1-test">Notify message</h1>
       <p id="p-test">It's notify message</p>
     </div>
  </body>
</html>


Для целевых объектов мы назначили id, чтобы быстрее их отыскать в DOM-структуре. Постфикс -test призван обозначить, что id предназначен лишь для целей тестирования. Открываем наш любимый Firefox с Selenium IDE и вводим следующий тест (исходники тестов, которые Selenium IDE сохраняет в HTML-формате, приводятся без тегов, чтобы улучшить читаемость):

project.localhost/tests/selenium-tests/notify-message-test.html
open
   /tests/html-examples/notify-message-example.html
verifyEval
  window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('color');
  green
verifyEval
  window.getComputedStyle(window.document.getElementById('p-test'),null).getPropertyValue('color');
  green
verifyEval
  window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('font-size');
  20px
verifyEval
  window.getComputedStyle(window.document.getElementById('p-test'),null).getPropertyValue('font-size');
  14px


Запускаем


[error] Actual value 'rgb(0, 128, 0)' did not match 'green'
[error] Actual value 'rgb(0, 128, 0)' did not match 'green'

Оказывается getComputedStyle() возвращает цвета в виде rgb(r,g,b). Наверное, это правильно. В тестах не будет путаницы, если верстальщик будет использовать разные обозначения для одного и того же цвета. Корректируем тест.

project.localhost/tests/selenium-tests/notify-message-test.html v2
open
   /tests/html-examples/notify-message-example.html
verifyEval
  window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('color');
  rgb(0, 128, 0)
verifyEval
  window.getComputedStyle(window.document.getElementById('p-test'),null).getPropertyValue('color');
  rgb(0, 128, 0)
verifyEval
  window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('font-size');
  20px
verifyEval
  window.getComputedStyle(window.document.getElementById('p-test'),null).getPropertyValue('font-size');
  14px



Включаем режим паранойи


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

project.localhost/tests/html-examples/clear-example.html

<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/css/styles.css">
  </head>
  <body>
    <h1 id="h1-test">Not message</h1>
    <p id="p-test">It is not message</p>
  </body>
</html>


project.localhost/tests/selenium-tests/clear-test.html
open
  /tests/html-examples/clear-example.html
verifyEval
  window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('color');
  rgb(0, 0, 0)
verifyEval
  window.getComputedStyle(window.document.getElementById('p-test'),null).getPropertyValue('color');
  rgb(0, 0, 0)
verifyEval
  window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('font-size');
  32px
verifyEval
  window.getComputedStyle(window.document.getElementById('p-test'),null).getPropertyValue('font-size');
  16px


Запускаем.



Будьте внимательны – тесты могут не пройти, в зависимости от настроек браузера, но ведь на то оно и тестирование.

Делаем рефакторинг


Тесты написаны, что дальше? Дальше – все преимущества разработки с применением автоматизированного тестирования. Например, рефакторинг. Посмотрев на наш CSS файл, мы видим, что две строчки, касающиеся зеленого цвета повторяются. Попробуем это исправить. Пусть мы не совсем уверены в знании правил CSS, которые запомнить еще труднее, чем понять. Теперь нас это не остановит. Ведь наш код под защитой тестов.

project.localhost/css/styles.css v2
div.notify-message h1{
  color:green;
}

div.notify-message h1 {
  font-size: 20px;
}

div.notify-message p {
  font-size: 14px;
}



[error] Actual value 'rgb(0, 0, 0)' did not match 'rgb(0, 128, 0)'

Ах, да!!! Забыли при копировании подправить селектор.

div.notify-message h1 {
color:green;
}



Тест пройден.

Разработка CSS в стиле TDD


Теперь мы можем полностью применять концепции Test Driven Development. Предположим, что поздно вечером (как обычно, перед деадлайном) к web-верстальщику приходит web-программист и начинает что-то невнятно бормотать и делать руками неопределенные жесты. (Прошу программистов не обижаться – это камень и в мой огород, так как мне приходится быть и тем и тем). Из спутанного рассказа становится ясно, что нужно еще и выводить сообщения об ошибке, которые должны быть красного цвета. При просмотре файлов проекта оказывается, что теперь файл index.html имеет следующий, никем не предугаданный, вид:

project.localhost/index.html v2
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/css/styles.css">
  </head>
  <body>
    <h1>Super project</h1>
    <p>It's really very big and complex project !</p>
    <div class="notify-message">
       <h1>Hi,Guest !</h1>
       <p>Welcome to our beautiful word!</p>
     </div>
    <div class="error-message">
       <h1>You are not authorized.</h1>
       <p>Please login or register now.</p>
     </div>
  </body>
</html>


Оторвавшись от просмотра телесериала “Бэтмен навсегда”, засучив рукава, сохраняя по возможности хладнокровие, в соответствии с концепцией "сначала тесты", web–верстальщик создает файл:

project.localhost/tests/html-examples/error-message-example.html
<html>
  <head>
    <link rel="stylesheet" type="text/css" href="/css/styles.css">
  </head>
  <body>
    <div class="error-message">
       <h1 id="h1-test">Error message</h1>
       <p id="p-test">It's error message</p>
     </div>
  </body>
</html>


И добавляет в Selenium IDE еще один тест:

project.localhost/tests/selenium-tests/error-message-test.html
open
  /tests/html-examples/error-message-example.html
verifyEval
  window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('color');
  rgb(255, 0, 0)
verifyEval
  window.getComputedStyle(window.document.getElementById('p-test'),null).getPropertyValue('color');
  rgb(255, 0, 0)
verifyEval
  window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('font-size');
  32px
verifyEval
  window.getComputedStyle(window.document.getElementById('p-test'),null).getPropertyValue('font-size');
  16px


[error] Actual value 'rgb(0, 0, 0)' did not match 'rgb(255, 0, 0)'
[error] Actual value 'rgb(0, 0, 0)' did not match 'rgb(255, 0, 0)'
[error] Actual value '32px' did not match '20px'
[error] Actual value '16px' did not match '14px'

В соответствии с классикой TDD тест не проходит. Дорабатываем CSS.

project.localhost/css/styles.css v3
div.error-message {
  color:red;
}

div.notify-message {
  color:green;
}

div.notify-message h1, 
div.error-message h1  {
  font-size: 20px;
}

div.notify-message p, 
div.error-message p {
  font-size: 14px;
}




Тесты пройдены.

Развитие без препятствий


Приведенный пример элементарный, но думаю, что его можно масштабировать на большие проекты. Кроме очевидных выгод от автоматизированного тестирования есть еще положительные моменты. Постепенно в каталоге html-examples начнут накапливаться тестовые html-файлы. Их пользу трудно переоценить. Это - запротоколированные прецеденты использования html в проекте. Этими файлами можно пользоваться в спорных ситуациях, когда неясно кто прав – программист или верстальщик. Когда в команду придет новый человек, эти файлы здорово помогут войти в курс дела и в правила разработки.

В заключение приведу девиз TDD-разработчиков, который мне очень нравится:



Желаю Вам успешных проектов, красивых CSS-файлов и спокойного сна по ночам.

Similar posts

AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 12

    +2
    verifyEval
    window.getComputedStyle(window.document.getElementById('h1-test'),null).getPropertyValue('color');
    green


    Не вижу смысла в таких тестах.

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

        1. Тестирование CSS не имеет смысла, так как это декларативный язык, а его результатом является сверстанное изображение страницы, которое можно оценить лишь визуально.
        2. Протестировать CSS можно с помощью снятия битмапов с сгенерированной страницы и сверка ее с эталонным изображением. Для этого даже есть некоторые инструменты.


        Конечно, во многом Вы правы. Метод, описанный в данной статье, не идеален. Но и совсем бессмысленным, его я бы не назвал. Если удается протестировать хоть что-то — это уже пусть небольшое, но достижение. Здесь мы полностью можем протестировать правильность селекторов и механизм каскадирования. У нас появляется мало-мальски твердая почва для проведения рефакторинга. Мы можем легко вычислить мусор и балласт и удалить его. А это по-моему, немало.

        Теперь насчет сползания элементов. Ваш комментарий заставил меня немного поискать в Интернете, и вот, что я нашел:

        Одна из самых распространенных задач — определение абсолютной позиции элемента относительно левого верхнего угла документа.
        Для определения позиции используются следующие свойства элемента:
        offsetTop — отступ сверху, измеряется в пикселах относительно родительского элемента.
        offsetLeft — отступ слева, измеряется в пикселах относительно родительского элемента.
        offsetParent — ближайший родитель, относительно которого делается отсчет. Его значение будет null если текущий элемент невидим (display: none) или это корневой элемент документа.
        Поскольку значение считается от ближайшего родителя, то абсолютная позиция относительно верхнего левого угла документа обычно считается в цикле, который завершается тогда, когда значение offsetParent будет равно null.


        Таким образом, мы через JavaScript в наших тестах можем контролировать и взаимное расположение элементов, хотя это будет чуть сложнее, чем цвет или параметры шрифта.
          +2
          Тут главная проблема в муторности и неестественности написания таких тестов. Овчинка совершенно не стоит выделки.
            0
            Согласен, но с некоторой оговоркой. В БОЛЬШИНСТВЕ случаев овчинка не стоит выделки. И дело, мне кажется, больше в овчинке. Тесты всегда может показаться писать муторно. Проблема кроется в том, что в большинстве проектов, где используется CSS, ошибка в CSS-коде не критична. Ну съехало там чего-то куда-то. На скорость не влияет.

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

            Автоматизация не есть панацея. Как правильно сказал murr «Видимо у автора очень много свободного времени.» То время которое вы тратите на написание этих тестов не окупится в реальном проекте, не говоря уже просто о целесообразности таких тестов.
          +7
          Видимо у автора очень много свободного времени. Вы сами же описали, почему такое занятие, как тестирование css не имеет смысла, в том числе по сравнению изображений (pixel perfect — это сказки для людей, которые не понимают, к примеру, как рендерется шрифты на разных платформах, малейшее изменение длины текста — и привет).

          К тому же, вы тестируете отображение в одном, не самом популярном, браузере в одной ОС.

          Если уже собрались автоматизировать тестирование представления, то делайте это пожалуйста на реальном проекте, а не на кошках. Какой смысл тестировать специально написанную для теста страницу? Или вы решили протестировать движки браузеров?

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

          Т.е. вы фантастически замедляете прогон тестов на проекте, при этом не внеся никакой ощутимой пользы от тестирования: вы не сможете отследить ситуации, когда элементы поехали от разного количества вложенных элементов, вы не сможете отследить правльное применение всевозможных хаков, когда ваш js будет падать в тестовом режиме, вам прийдется ковырятся, разбираясь, почему это фейсбучные скрипты или еще какая-то подобная какашка не успели догрузится в предложенные вами в идеальном мире 20 секунд. На таком уровне покрытия тестами невероятно сложно формализовать машине, какой именно вы представляете страницу. Я молчу про поддержку этого добра на больших проектах, особенно молчу про проекты с частыми редизайнами.

          Если у вас есть лишнее время и желание сделать проект лучше, посмотрите лучше в сторону capybara, ознакомившись с хорошими практиками.
            0
            Спасибо за наводку. Capybara обязательно посмотрю. Я сейчас копаю в сторону Selenium. Если Вы знаете, то Selenium IDE — это вершина айсберга. Есть еще Selenium-сервер. Из Selenium IDE можно одним кликом получить тесты на Вашем любимом языке для xxxUnit и прогнать их на большинстве браузеров.

            И еще. Я с Вами вынужден согласится, что данная статья носит больше теоретический характер. Даже очень теоретически. И конечно, я представляю, что такое настоящие проекты с постоянной нехваткой времени и ресурсов. Скорее всего, в реальном проекте проводить тестирование CSS будет экономически невыгодно. А так жаль…
              +3
              Selenium IDE — это вершина айсберга.

              И лучше её не трогать, а писать нормальные тесты через биндинг к любому языку с использование нужных паттернов.
              Automated test script recorders (like Selenium IDE) are for dummies. (с) watirwebdriver.com/
            0
            Классная статья! Если будет возможность — очень хотелось бы увидеть продолжение с типовыми сценариями тестирования — сетка, типографика, цвета, в общем то что пригождается практически в каждом проекте.
            • UFO just landed and posted this here
                0
                Согласен. Правильная методология значительно ускоряет и повышает надежность написания кода. Мы можем уменьшать взаимовлияние участков кода друг на друга (так сказать, изолировать простанства состояний). На это, конечно, нацелены все современные технологии создания ПО. Но не думаю, что во многих случаях можно так просто удастся избавится от каскадирования (ведь не зря же его придумали). И не думаю, что правильная методология исключает необходимость тестирования. Спасибо за комментарий, мне он очень понравился. Я и сам об этом всем много думал. Но ведь все закоулки мысли в статье не опишешь, иначе она будет трудна для чтения.

                О БЭМ, признаться, ничего не знал. Наверное, Яндекс плохого не придумает. Буду изучать.

              Only users with full accounts can post comments. Log in, please.