Я все больше в своей практике пытаюсь использовать автоматизированное тестирование. Стараюсь при этом не плодить инструменты и библиотеки, обходиться простыми подходами. Не так давно, я задумался о том, как протестировать CSS-файлы. Поиск по Интернету выявил следующие точки зрения на этот вопрос:
- Тестирование CSS не имеет смысла, так как это декларативный язык, а его результатом является сверстанное изображение страницы, которое можно оценить лишь визуально.
- Протестировать CSS можно с помощью снятия битмапов с сгенерированной страницы и сверка ее с эталонным изображением. Для этого даже есть некоторые инструменты.
- Нашлась некоторая библиотека CSS-Unit.
Должен сказать, что все варианты мне не понравились. В конечном итоге мне удалось протестировать CSS используя текстовый редактор, Firefox + Selenium IDE и… и больше ничего.
Немного теории
Наиболее конструктивные идеи мне принесла длительная медитация над вкладкой Firebag «Cкомпилированный стиль».
По содержимому этого окна стало ясно, что браузер генерирует страницу как минимум в два этапа.
- Определение всех параметров всех элементов, на основе CSS
- Верстка страницы, на основе полученных значений
Если второй этап никак не протестировать, кроме как битмапами, то первый, скорее всего, вполне поддается автоматизированному тестированию. А ведь именно на первом этапе возникают почти все ошибки в написании 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-серверу, и настраиваем к нему доступ по адресу http://project.localhost
.
В корне, по давно сложившейся привычке, заводим файл index.html
, который делает наш проект похожим на реальный, а также символизирует исходный код проекта, безжалостно изменямый программистами независимо от нашей воли и желания. Одновременно заводим файл styles.css
, который содержит css-код который мы будем разрабатывать и тестировать.
Предположим, в нашем проекте нам нужно выводить уведомляющие сообщения. Они должны выводится зеленым шрифтом. Сообщение состоит из заголовка <h1>
и основного текста <p>
. Шрифт заголовка 20px
, основного текста 14px
. Для размещения сообщений программисты зарезервировали контейнер <div class="notify-message">
. В соответствии с этой ситуацией наши файлы index.html
и styles.css
на момент начала тестирования имеют следующий вид:
http://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>
http://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
http://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-формате, приводятся без тегов, чтобы улучшить читаемость):
http://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)
. Наверное, это правильно. В тестах не будет путаницы, если верстальщик будет использовать разные обозначения для одного и того же цвета. Корректируем тест.
http://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
Включаем режим паранойи
Естественно, к этому тесту наиболее въедливые тестировщики найдут что добавить. В частности, можно сделать проверку, что наши стили не затрагивают чистых <h1>
и <p>
.
http://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>
http://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, которые запомнить еще труднее, чем понять. Теперь нас это не остановит. Ведь наш код под защитой тестов.
http://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
имеет следующий, никем не предугаданный, вид:
http://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–верстальщик создает файл:
http://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 еще один тест:
http://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.
http://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-файлов и спокойного сна по ночам.
Автор: headfire