В современном вебе несправедливо мало внимания уделяется хоть сколько-нибудь автоматизированному тестированию UI. Особенно это касается статической вёрстки. На проекте 2ГИС Онлайн мы попытались частично восполнить этот пробел. Какие полезные практики мы приобрели, и о каких хороших библиотеках мы узнали, расскажем далее.
Верстка
Рассмотрим историю одной выдуманной кнопочки:
Квадратная кнопка с иконкой. Всё просто. Размеры можно хардкодить в пикселях — ведь какая разница?
Практически сразу кнопка начинает эволюционировать. Пользователи редко нажимают на кнопку. Что же делать? Как вариант — добавить поясняющую надпись. Осталось лишь чуть увеличить ширину, чтоб надпись входила:
А как же надпись на русском языке? Да и оригинальный текст может измениться. Так что хардкодить ширину нельзя, придется делать произвольную:
Текста может быть так много, что в одну строку он не читается. Нужно ограничить максимальную ширину и развязать высоту:
Кнопкой по-прежнему мало пользуются! Можно сделать вспомогательное многострочное описание. Для этого нужно добавить span-чик с соответствующими стилями:
Кажется, уже всё учтено, и никто не в силах сломать эту вёрстку. Никто кроме одиннадцатиклассницы — длинного слова, не помещающегося в максимальную ширину. Просто делаем перенос по буквам:
Пользователи стали жаловаться на навязчивую большую кнопку, поэтому было принято решение сделать крестик, закрывающий кнопку без фактического нажатия. Немного стилей для крестика, чуточку отступа для заголовка чтоб он не попадал под этот крестик — и всё готово:
Не забываем, что основного текста может и не быть, правим отступы:
Кажется теперь мы имеем многофункциональную кнопку на все случаи жизни, с поправленными багами, правда? Давайте ещё раз посмотрим все кейсы, только теперь одновременно:
Что же мы видим? Вместо полнофункциональной кнопочки мы видим обилие регрессионных багов: иконка вывалилась из кнопки, поломалось вертикальное выравнивание, появился лишний отступ для крестика… Иными словами, хотели как лучше, получилось как всегда.
А теперь представьте, что речь не о кнопке, а о большом, постоянно развивающемся проекте с многомиллионной аудиторией. Поддержка превращается в ад, в котором, к сожалению, живут многие верстальщики.
Есть множество техник и методологий, помогающих поддерживать вёрстку сложных проектов. Например, широко известный всем БЭМ, который мы тоже используем. Но сама по себе методология БЭМ не решает, а только локализует регрессионные баги вёрстки. Как же избавиться от них в принципе? На самом деле очень просто (и для этого вам не понадобится изучать API десятка новых js-библиотек!) — нужно создать одну тестовую html-страничку со всеми состояниями кнопки. Давайте дружно, прям сейчас, создадим её, это не займёт больше 10 секунд. Вот вам даже разметка:
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<!-- Стилевой файл проекта -->
<link rel="stylesheet" href="/style.css">
<title>Test page</title>
</head>
<body>
<!-- Здесь будет первое состояние -->
</body>
</html>
Никакого js, npm-зависимостей, express-серверов… Просто html-файлик, открытый в браузере.
На каждую эволюционную итерацию нужно заводить одно или несколько новых состояний (то есть вариантов html-кода тестируемого элемента, включая контент), но не удалять старые. Просмотр всех старых состояний можно назвать полуавтоматической регрессией.
Мы намеренно не будем советовать каких-либо средств автоматизации этой работы, по двум причинам: во-первых, для большинства случаев создать самостоятельно такую страничку может (и должен) каждый фронтэндер; во-вторых, для сложных случаев, и случаев pixel-perfect, мы пишем собственное решение, которое пока не готовы анонсировать.
Одиннадцатиклассница
Более половины багов вёрстки связаны с очень простым просчетом: фронтэндер не учитывает, что в текстовые ноды его макета может попасть, вообще говоря, любой текст. Он может быть многострочный, состоять из длинных слов, или может быть пустой строкой.
Возьмите пару любых своих макетов — готов поспорить на российский рубль, что их сможет сломать даже одна одиннадцатиклассница. Вставьте несколько одиннадцатиклассниц в каждое поле — поломка вёрстки гарантирована!
Предлагаю открыть любой популярный сайт и в отладчике выполнить следующий код
var a,w=document.createTreeWalker(document,NodeFilter.SHOW_TEXT);while(a=w.nextNode()){a.textContent='Одиннадцатиклассница пошла посмотреть на достопримечательность, она шла долго, несколько строчек, пока не пришла'}
Первый десяток сайтов, который пришел на ум (исключая 2ГИС Онлайн, конечно), был сломан до такой степени, что пользоваться ими стало абсолютно невозможно.
Конечно, это не значит, что одиннадцатиклассница каждый день ходит по всем текстовым нодам интернета и ломает вёрстку (с тем же успехом сайт можно защищать от эболавируса), но вы должны учитывать следующие факторы:
- Если ваш проект существует на нескольких языках, то на другом языке может быть более длинная фраза (русский длиннее английского, испанский длиннее русского и т.д.)
- У пользователя может не быть основного шрифта, а fallback окажется крупнее
- У пользователя может быть кастомный зум или иной механизм рендеринга шрифтов
- Текст может быть изменён в процессе поддержки, в том числе контент-менеджерами, которые вёрстку проверять уж точно не будут
- В поле может прилететь неправильный текст просто из-за бага в js-коде
- Баг в вёрстке другого блока может ужать ваш блок
- Кто-нибудь прочитает эту статью и выполнит приведённый код в отладчике :)
Мы искренне верим, что в любом из этих случаев весь текст должен быть виден пользователю, и он должен быть читаемым. Он может некритично менять размер блока, уходить в фейд или обрываться многоточием; но он не должен превращаться в атакующего соседние ноды монстра, выжигающего глаза пользователя; то есть он как минимум не должен налезать на другой текст.
Автоматизация
Всё, о чем мы писали выше, назвать автоматическим тестированием нельзя, потому что требует глаз человека. Если вы не хотите напрягать свои глаза совсем, вам помогут тесты, которые мы называем dom-тестами.
Dom-тест — это js-код, выполняемый в браузере, который что-то проверяет в изолированной части приложения, или во всём приложении целиком. Проверяться может что угодно: от наличия класса и атрибута в html, до аргументов, с которыми была вызвана какая-то функция, в том числе асинхронная.
Здесь вам помогут такие замечательные библиотеки как:
mocha. Фреймворк для тестирования. Позволяет создавать тесты и группы тестов, включая асинхронные; выполнять какой-то код перед тестом или группой тестов, и после него.
chai. Библиотека для assert-ов принципе можно использовать нативный assert node.js, но у “чая” есть плюшки, например красивый diff deepEqual.
sinon. Библиотека, позволяющая делать две важных вещи:
- Следить за функциями. Вы будете знать, сколько раз и с какими аргументами была вызвана функция, за которой вы следите.
- FakeTimers. Позволяет подменять setTimeout и setInterval и, таким образом, “управлять” временем. То есть, после подмены вы может вызывать sinon.tick(20) — и мгновенно пройдет 20 миллисекунд времени, при этом выполнятся все таймауты, зарегистрированные на выполнение в этом периоде.
Комбинируя всё это можно писать тесты как на все приложение, так и на его изолированные части. В этих тестах можно делать буквально всё: заполнять форму и кликать в кнопки; менять dom-дерево; делать ajax-запросы… По сути, внутри таких тестов вы находитесь внутри отладочной консоли браузера, а в руках у вас любые js-библиотеки для тестирования, которые вам нравятся.
Преимущество таких тестов — они выполняются в браузере, а значит, могут быть запущены в любом браузере с нулевой затратой ресурсов на адаптацию (чего не скажешь о драйверах selenium, особенно в комбинации, допустим, с ie8 или android 4.0). Кроме того, такие тесты можно запускать в полностью автоматическом режиме на phantomJS, например на git-push hooks.
На всякий случай стоит отметить, что dom-тесты не являются альтернативой регрессионному тестированию вёрстки; это просто ещё один подход защиты кода, со своей спецификой и областью применения.
Выводы
Заведите html-страничку для регрессии вёрстки, пустите в неё одиннадцатиклассницу, и ваша вёрстка станет в 10 раз качественнее. Защищайте свой код js-тестами, и вы сможете отказаться от услуг тестировщиков (которым придётся переквалифицироваться в автоматизаторов и помогать вам писать тесты). На проекте 2ГИС Онлайн 90% задач попадают на бой без ручного тестирования, и это во многом стало возможным благодаря озвученным выше подходам тестирования.
Не дожидайтесь, когда одиннадцатиклассница придёт к вам сама!
Больше информации можно почерпнуть из моего выступления на Web Standards Days.
Автор: Diokuz