Привет, читатель! Если доводилось писать 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
Leiningen — менеджер зависимостей, как npm из мира Node.js. С тем отличием, что, как зависимость, Leiningen преподносит ещё и сам интерпретатор Clojure. Поэтому нам достаточно будет установить Leiningen, Clojure он скачает сам.
Основной репозиторий Clojure, так же можно использовать библиотеки из maven-репозитория (несметные богатства Java-сообщества). Подборка полезных библиотек.
После этого можно создать новый Clojure-проект, набрав в консоли:
lein new app example-selenium-project
Leiningen создаст нам всё дерево проекта и конфигурационный файл — project.clj, (в npm его аналог — package.json).
Таже в Leiningen очень хорошо реализована работа с профайлами, определив ряд своих профайлов в разделе :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.
Комментарии (11)
youlose
23.08.2016 10:05+1Я на Ruby пользюусь selenium-webdriver + pry (это REPL). Тоже интерактивно, почему именно Clojure (его тоже сейчас изучаю, хорошо мозги вправляет =) ) удобнее, так пока и не ясно.
Source
23.08.2016 20:03Согласен. В статье вместо демонстрации сильных сторон Clojure, идёт сравнение тёплого (Selenium WebDriver) с мягким (Selenium IDE). Ну а REPL'ом давно уже никого не удивить )))
Код теста в примере пока что выглядит как-будто его с Java 1-в-1 переписали, не говоря уж о том, что запуск браузера, логирование и подсчёт результатов — это не обязанность конкретного теста.
На мой вкус этот тест (без потери функционала) должен выглядеть примерно так:
(selenium-test gosuslugi-main-search-form "https://www.gosuslugi.ru/" (->> ($ ".index-slider-search input") (type-text "загранпаспорт") (->>keys Keys/ARROW_DOWN) (->>keys Keys/ARROW_DOWN) (->>keys Keys/ENTER)) (is (there ($ ".title_search"))))
seryh
24.08.2016 13:24+2Спасибо за замечание к тесту. Сделал комит с соответствующим макросом. Касательно сравнения тёплого с мягким, нужно было найти баланс, между полезностью и доступностью для новичков, все таки clojure далеко не мэйнстримовый язык.
Source
24.08.2016 14:36Спасибо, так лучше, но всё ещё осталось управление потоком выполнения через исключения.
А по поводу доступности для новичков я бы ещё Вам попенял за название "->>keys", которое для новичка выглядит как-будто вариант использования "->>", лучше было бы банальное «send-key» или «press-key»
RockPresident
26.08.2016 14:26+1В сравнении с чем clojure помогает ускорить написание тестов? Ответа на данный вопрос в статье так и не нашёл. (Половина статьи вообще о самом Selenium, что к тезису про clojure как я понимаю отношения не имеет.)
Императивный стиль, который мифически вбивается кому-то со школы — уже давно не 100 процентно императивный, в большинстве высокоуровневых языков. Много где есть уже функции первого порядка, динамическое программирование и т.д. Может быть когда-то лисп и отличался подобными фичами и действительно имел смысл. Сейчас — все эти фичи как я понимаю уже встроили в более развитые и распространённые языки.
Так зачем нужен clojure и чем он быстрее в написании например JavaScript, Python, Ruby или там Java/Groovy/Scala (все из которых могут похвастаться более привычным синтаксисом, большим обилием документации и инструментов, стабильностью (по сравнению с clojure))?
Пока что такое впечатление что вы изучаете clojure и вам просто интересно изучить что-то новое. Но зачем же вы говорите тогда что оно быстрее? Так и напишите что это всё "для прикола", а не для какой-то эффективности.seryh
26.08.2016 14:48+1>Так зачем нужен clojure и чем он быстрее в написании например JavaScript, Python, Ruby или там Java/Groovy/Scala.
Быстрее в первую очередь — REPL'лом, на него и сделан акцент в статье, к сожалению в статье видимо не удалось ясно показать чем REPL Clojure удобнее остальных REPL присутствующих в других языках. Его преимущество кроется в мелких деталях завязанных на фичах языка — иммутабельность, функциональность, чистые функции и многое другое. Как эти тонкие моменты передать в статье, я не знаю, все это познается на опыте. Если вы сможете сделать подобное сравнение, пусть даже не в пользу clojure, буду рад почитать.Source
26.08.2016 15:31+1Просто REPL'ом сейчас никого уже не удивишь. Мощь Lisp в том, что это метаязык с неограниченными возможностями (ну или ограниченными возможностями виртуальной машины, как в случае с Clojure). За счёт этого можно более изящно выражать свои намерения в коде. Проблема в том, что при переходе с мейнстрим-языков приходится долго въезжать в тему. И поначалу код получается похож на то, что было в привычном ЯП, только со странным синтаксисом. Примеры такого кода обычно отталкивают читателей от идеи изучить Lisp :-)
MadridianFox
А вы уверены что написание тестов на clojure будет быстрее чем на родном для разработчика языке? Если для перехода с php на java или js надо привыкнуть к синтаксису и учесть нюансы, то для того чтобы свободно писать на clojure, необходимо хорошенько так сдвинуть мозг.
Однако не отрицаю, что clojure'вский repl положенный на разные предметные области даёт очень резкий вау-эффект, и экономит кучу времени.
comerc
Я сейчас так влюблён в Clojure, что не способен объективно отвечать!
MadridianFox
Да я и сам уже несколько недель втайне от своего основного ЯП, по вечерам встречаюсь с clojure. И как раз поэтому могу сказать что нельзя просто так взять и сразу написать что-то адекватное на cjojure, так же, как это можно было бы сделать с более привычными языками.
yamatoko
а вы уверены, что любой китаец будет говорить на китайском быстрее и лучше, чем на его родном языке?