Как Clojure помогает ускорить написание Selenium-тестов
Привет, читатель! Если доводилось писать Selenuim-тесты чуть сложнее чем на пару полей ввода и одну кнопку, то эта статья может пригодиться.
Наверняка знакомо чувство неправильности происходящего, когда внезапно отказывался работать тест-кейс длиной в пару минут, вынуждая тыкаться вслепую, чтобы найти поломавшийся css- или xpath-селектор в сложном Single Page Application, раз за разом запуская этот медленный сценарий, только чтобы дождаться вывода лога в консоль. Оказывается, можно писать Selenium-тесты с комфортом!
Про Selenium. Когда-то это был просто плагин для FireFox. Нажимаешь кнопочку «запись», и начинаешь дергать интерфейсы тестируемого сайта (формочки, ссылки). Практически без знания программирования, полученный сценарий можно править, подставляя нужные значения проверок. Всё — тест готов! Запускай каждый раз после деплоя, и проверяй, чего сдвинул локтем. Потом в Selenium добавили API для разных языков. В частности есть и для JAVA. Через API можно делать тоже самое: запускать браузер с требуемым сайтом, проверять переданные значения в формах, ходить по ссылкам и т.д. Беда в том, что скрипт выполняется медленно. Чтобы тестировать тесты, надо перезапускать сценарий, а это форменная пытка. Clojure (поверх JAVA) позволяет выполнять куски кода в работающей программе посредством REPL прямо в редакторе!
Разбор проекта с Selenium-тестами
Проект доступен на GitHub, особенности:
- Кроссплатформенность — писать тесты можно под ОС Windows, с профайлом +windows, затем собирать jar файл под *nix, и выкладывать на сервер.
- Работа сразу с двумя web drivers: selenium и phantom, это удобно когда нужно визуально отладить тест в Selenium, скомпилировать jar, который работает с Phantom и залить его на Linux-сервер где его будет дергать какой-нибудь CI-скрипт.
В зависимостях проекта можно увидеть библиотеку — clj-webdriver, это основной инструмент для работы с веб-драйвером, документация к нему.
Разберем простой тест example-selenium-project.tests.gosuslugi-main:
(deftest gosuslugi-main-search-form
(profile/open-browser "https://www.gosuslugi.ru/")
(try
(->> ($ ".index-slider-search input")
(type-text "загранпаспорт")
(->>keys Keys/ARROW_DOWN)
(->>keys Keys/ARROW_DOWN)
(->>keys Keys/ENTER))
(when-not ($ ".title_search")
(throw (Exception. "redirect to search result, error")))
(swap! profile/tests-success inc)
(is true)
(log/info "gosuslugi-main-search-form -> ok")
(catch Exception e
(log/info "gosuslugi-main-search-form -> fail" (.getMessage e))
(swap! profile/tests-fail inc)
(is false))))
(deftest gosuslugi-main-search-form ...)
deftest
— макрос, импортируется из библиотеки clojure.test
. Это встроенная в Clojure библиотека для написания тестов. Имеет стандартный для тестов функционал. Макрос deftest
просто создает функцию.
Сам тест обернут в (try ... catch)
для удобства, чтобы прерывать выполнение формы вызовом Exception
, или же в любых нестандартных случаях (например, если не найден элемент), и выполнять форму в catch
, обрабатывающую провал теста.
(->> ($ ".index-slider-search input") ...)
->>
— стандартный Clojure-макрос для написания более читабельного кода «шиворот навыворот». Без него сценарий выглядел бы так:
(->>keys Keys/ENTER
(->>keys Keys/ARROW_DOWN
(->>keys Keys/ARROW_DOWN
(type-text "загранпаспорт" ($ ".index-slider-search input")))))
Макрос ->>
принимает список форм и раскрывает их, передавая последовательно результат выполнения, как последний аргумент, следующей форме. Чтобы использовать макрос ->>
, я сделал несколько функций-обёрток, которые следуют простому соглашению — принимают последним элементом объект element, и возвращают его же.
(swap! profile/tests-success inc)
и (swap! profile/tests-fail inc)
— простые счетчики успешных и проваленных тестов.
Почему Clojure?
Простой ответ — уровень абстракции над сложностью. Должно быть ты уже сходил в Википедию, посмотрел на синтаксис этого Lisp-диалекта, и проходишь первую стадию принятия неизбежного. Пройдёшь её или нет — дело твоё, могу только дать несколько советов, основанных на опыте.
- Clojure — легкий в изучении язык, более высокий порог входа связан с его отличием от императивного подхода, который нам вбивают в голову со школы. Переборов закостенелость сознания, мы увидим что Clojure превращается в удобный инструмент, позволяющий упростить разработку приложения.
- Как же не запутаться в таком обилии скобок? Особых проблем со скобками нет, к ним привыкаешь за день, когда понимаешь их назначение. Так же в редакторах появился ряд хороших инструментов, которые облегчают инкубационный период Clojure-разработчика. Во-первых, прекрасный Parinfer, доступен для большинства редакторов. А во-вторых, банальное включение в настройках редактора радужных скобок.
- Для написания тестов не понадобится доскональное знание Clojure. Трансдьюсеры, редьюсеры и прочая муть — изучение этого можно отложить на потом. Для старта достаточно освоить базовые вещи за пару-тройку вечеров.
Быстрый старт по инструментам разработки
Если бы ты провел молодость будучи хиппи в Америке 60-х годов, то мог вкусить дух свободны, демократии и лёгких наркотиков; если не довелось, ничего страшного — этот дух ты можешь ощутить используя в работе REPL Clojure.
REPL Clojure — выводит написание Selenium-тестов на совершенно иной уровень! Ключевая особенность этого REPL от любых других в том, что можно писать код в уже работающем приложении, не теряя его состояний. Этот инструмент позволяет не просто составлять рабочие css- или xpath-селекторы, но и проводить тесты в каком угодно порядке, и проверять работоспособность любого из узлов большого тест-кейса не теряя состояния сложного Single Page Application.
Выполнение кода нашего проекта построчно посредством REPL в редакторе LightTable
Редактор кода для Clojure — почти под все популярные редакторы и IDE существуют плагины для поддержки Clojure, я остановился на IntelliJ IDEA + Cursive, как имеющий наименее низкий порог входа. Небольшая инструкция по настройке и использованию REPL в Cursive.
Также популярны:
- Emacs + Cider (Spacemacs)
- Atom + Proto REPL
- LightTable
Leingen — менеджер зависимостей, как npm из мира Node.js. С тем отличием, что, как зависимость, Leingen преподносит ещё и сам интерпретатор Clojure. Поэтому нам достаточно будет установить Leingen, Clojure он скачает сам.
Основной репозиторий Clojure, так же можно использовать библиотеки из maven-репозитория (несметные богатства Java-сообщества). Подборка полезных библиотек.
После этого можно создать новый Clojure-проект, набрав в консоли:
lein new app example-selenium-project
Leingen создаст нам всё дерево проекта и конфигурационный файл — project.clj, (в npm его аналог — package.json).
Таже в Leingen очень хорошо реализована работа с профайлами, определив ряд своих профайлов в разделе :profiles
файла project.clj, можно очень гибко разделять работу программы в окружениях develop или production, и не только.
Можно запускать приложение с любым количеством профайлов, например:
lein with-profile +windows,+selenium run
Или скомпилировать jar с нужными профайлами:
lein with-profile +windows,+phantom uberjar
Заключение
Рискну предположить что Clojure является на сегодня наиболее удобным инструментом для написания Selenium-тестов. Благодаря двум факторам: интерактивному REPL и макросам, которые позволяют создать свой собственный лаконичный DSL-подпроект. Как показывает практика, можно не знать сам язык Clojure и успешно работать с DSL на фасаде. Это открывает неплохие возможности к быстрому подключению новых специалистов к написанию и поддержке тестов.
Полезные ссылки
- Видео-курсы по Clojure.
- Slack-чатик — инвайт можно получить тут, подключайтесь к #clojure-russia каналу!
- WebMeetups > clojure-russia — видео-записи тусовок.
yashaka
у меня есть подозрения что за трай кетч в тест логике могут не слабо накинуться… любые сложные конструкции типа ифов, циклов, трай кетчов — считаются плохим тоном в написании тестов
в нашем примере — это ведь функционал логирования по сути, и такие вещи обычно выносят в какие то абстракции…
может если есть желание показать удобство, то стоит показать как вынести такой код логирования в какую то — то ли функцию толи макрос дополнительный вокруг deftest
seryh
ну юнит тесты и селениум тесты, разные вещи. на практике try в селениуме очень удобен. так как почти все отваливания тестов происходят из за устаревших селекторов (edited) или еще какой внезапной фигни которую сложно предсказать
yashaka
логика «не использовать сложные конструкции» растет от того факта что тестов много, они могут менятся, и их пишет большое количество людей — поэтому нет времени сильно вдумываться в флоу — тесты должны быть буквально очевидными
по крайней мене насколько я это себе понимаю:)
в селениум тестах — это еще более важно, так как тесты сложнее сами по себе…
seryh
да простор для улучшения большой) руки пока не дошли
yashaka
или еще какой внезапной фигни которую сложно предсказать
внезапная фигня — это в любом случае эксепшен
и полетевший эксепшен — это уже упавший тест с репортом
просто встроенные в селениум эксепшены — мало информативны
поэтому люди и пишут свои врапперы вокруг селениума
как бы потому — что бы тест логика оставалась простой и очевидной…
seryh
я там в статье отметил в заключении что можно dsl написать простой
а в примере, так наколенная поделка
yashaka
я сам автоматизатор, а не разработчик, и вишу тут в чатах на эту тему,
и там как только какой то новичок показывает тест с ифами и трай кетчами — сразу льются горы нравоучений…
здесь суть не в том что хорошо, а что плохо…
, а в том что и так у нас посыл громкий как для неформального языка, и народ начнет лить критику :)
и получается мы им с нашими «наколенными подделками» только еще больше повода даем
У меня сейчас завал, поэтому к сожалению нет времени на то что бы помочь… Но если мы не спешим, то где то через недельку-вторую, я смог бы поконтрибьютить в эту статью…
motor4ik
, а кейс самый главный не рассмотрен в статье? что упал тест и как его реплом починить?
я так понимаю это была киллерфича статьи нет?
seryh
публикация на понедельник запланирована. вообще если взлетит то можно и вторую статью запилить
и свой dsl для тестирования запилить =Р
а так, небольшой примерчик сейчас, вполне понятный новичку
P.S. Я выступаю лишь в роли вдохновителя и редактора, автор статьи — seryh.