Как стать автором
Обновить

Комментарии 27

Тесты Selenium — зависимые тесты

Тесты должны быть независимыми друг от друга и подчищать за собой. Странно что этот базовый принцип вы открыли для себя с такой болью.
Тесты Selenium — медленные тесты

Они медленны настолько насколько медленно ваше приложение.
Selenium IDE — не помощник

Откройте для себя Developer tools различных браузеров и не мучайтесь с этим «IDE»
Получение элемента (findElement)

Откройте для себя Explicit Waits с ожиданием состояния.

WebDriver далеко не идеален, однако он вполне юзабелен. И я бы не стал говорить о том что вы «эффективно использовали» Selenium после фразы «больше всего страшно, что тестируемый web-клиент будет видоизменен». ;)
В самой статье я постарался описать те разочарования, которые ожидают разработчика, впервые столкнувшегося с подобной задачей. Так что тут вполне могут быть описаны наивные вещи.

Странно что этот базовый принцип вы открыли для себя с такой болью.

Мне стало больно скорее от того, что, конкретно в случае с Selenium, возможность «подчистить», в большинстве случаев, сводится к дополнительным шагам, аналогичным по природе тестовым действиям. А такие действия уязвимы для описанных проблем. Т.е. большинство раз тест отработает, но могут случиться исключения. Более того, такие неэффективные методы приносят дополнительные достаточно существенные временные затраты. Вот, видимо, такая ненадежность меня и удивила, во время первого опыта.

Откройте для себя Developer tools различных браузеров и не мучайтесь с этим «IDE»

Именно к этому я и пришел. Большую часть времени разработка проходит в процессе подбора XPath ключей, использовании различных плагинов, файр-багов и обычной JS консоли.

WebDriver далеко не идеален, однако он вполне юзабелен. И я бы не стал говорить о том что вы «эффективно использовали» Selenium после фразы «больше всего страшно, что тестируемый web-клиент будет видоизменен». ;)

На своем уровне познания Selenium я не смог придумать, как, не изменяя исходные кода веб-клиента, отвязать процесс тестирования от жестких завязок на структуру элементов. Я стараюсь их минимизировать, но идеальная ситуация, когда к любому элементу можно обратиться по уникальному ID. К сожалению, для меня это невозможно. По крайней мере, я не понимаю как это сделать :)
> Они медленны настолько насколько медленно ваше приложение.
Не совсем. У нас вот приложение быстрое, а тесты на селениуме гоняются долго. Почему? Потому что любой hover, нажатие на кнопку — передача сообщения по сети, выполнение в браузере (и так не для одного браузера) и ожидание ответа обратно. Если front-end'а и тестов на него в приложении много, тесты тоже будут выполняться долго, в сравнении с unit-тестами на порядок больше.
Сравнивать тесты через Selenium и простые юнит тесты по скорости полностью не корректно :) Говоря о быстроте приложения я, как ни странно, и имел ввиду отклик уже на стороне пользователя.
Недавно прикручивал selenium web driver к приложению. Под visual studio тесты гоняются на ура, изолированность присутствует, всё довольно быстро (конечно упирается в скорость приложения). Лучшим себя показал Chrome driver, а IE категорически отказывался делать клики элементам.

Это крайне утомительно, отладка подобна мукам, заставляет порой писать плохой код, но больше всего страшно, что тестируемый web-клиент будет видоизменен. Я гарантированно знаю, что он будет изменен. И это еще один болезненный момент.

Если id контролов гарантировано не поменяются, то большой проблемы в изменениях нет.
Для улучшения кода можно сделать карту страницы — описать класс с нужными контролами и их идентификаторами и обёртки по поиску элементов на страницах. Дальше работать со всем этим в «объектно подобном» стиле.
Я в конечном итоге пришел к функциональным слоям:
— абстрактный рутовый слой, содержащий всю низкоуровневую работу с веб-драйвером (построение, обертки над методами WebDriver API)
— слой навигации (просто набор методов goToMainPage, goToSearchTab)
— слой частых действий или действий, которые требуются более чем в одном тесте
— слой локаторов — огромный, строк на 300, справочник констант с объектами типа By. Есть желание сделать его вообще в виде внешнего property файла, но это уже детали.

Пока не могу сказать, насколько это удачное решение — видно станет, когда мне предстоит передать этот код на поддержку заказчику :)))
Понимаю и уважаю вашу боль…

У меня C#/NUnit/TeamCity

В процессе работы с Selenium я пришел к мысли, что не важно как получать доступ к WebElement-у, важно что и как с ним дальше делать. Исходя из этого, в своих тестах, я стараюсь делать сложные выборки с помощью JS кода. Выходит гораздо более стабильно и точно (хотя этому способствует ExtJS, на котором написан front-end). Даже если я получаю элемент, недоступный, скажем, для нажатия, то в момент нажатия правда вскрывается и тест законно падает. Зато бубнов нет с селекторами, ожиданиями и прочей болью.

Если посмотреть лог файл после тестов, то видно все низкоуровневые команды от Selenium к браузеру. Там видно как он делает это ожидание, так что ничего плохого в собственных велосипедах по этой части нету.

Все что пулается от Selenium к браузеру может потеряться. По этому не грех повторять действия, а на JS команды вешать хитрые маркеры, чтоб понять дошли ли они, интерпретировались ли.

Снимать скриншоты — великое добро.

В API, через которое пишут тесты, хорошо бы встроить дополнительную информацию о шагах теста. Помогает понять что происходило и где упало. Причем это можно формировать автоматически.

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

Лямбды, фанки и подобное сильно способствует ускорению теста за счет упрощения кодирования ожиданий.

«Error communicating with the remote browser» — встречал и под ChromeDriver :\ Честно сказать есть некоторый страх при обновлении драйвера или тестового браузера, что очень плохо.

Самая большая проблема возникла с разработчиками тестов. Я им предоставил API, они пишут тесты, тесты выполняются через NUnit. Проблема в том, что они пишут их как Unit тесты, а не как какие-то большие интеграционные тесты. Получается полная хрень, типа тест на 30 секунд, который проверяет нажимается ли кнопочка в выпадающем меню. При этом эта функция используется во всех тестах, но есть специальный тест, который типа это проверяет. В общем сдвинуть парадигму восприятия нереально трудно. С таким подходом тестовый билд (TeamCity) пухнет по времени до часу, а по факту проверяет элементарные, скучные вещи, причем стопицотраз дублировано. Беда.

Очень интересно как вы разворачивали Selenium Grid. У нас тесты есть, но мы их на гриде не параллели, скоро предстоит, может поделитесь опытом/ссылками? :)
По поводу Selenium Grid, все необходимое я нашел здесь:
code.google.com/p/selenium/wiki/Grid2

Принцип простой — в одной сети должны быть Selenium Hub и одна или несколько Selenium Node. По сути различия в структуре нет — это один и тот же jar файл, запущенный в разных режимах. (к примеру такой: selenium-server-standalone-2.30.0.jar).

Selenium Hub — точка, куда необходимо отправлять запрос на выполнение теста. Хаб распределяет задачи между своими нодами (исполнителями), учитывая наличие у них свободных слотов и сравнивая соответствия (capabilities) пришедшего теста и свободного слота. Если подходящий слот не найден — тест будет оборван.

Настройка всей сетки проста и не занимает много времени. Для профилирования тестов по отдельным браузерам надо создавать инстансы вебдрайверов — соответствующих этим браузерам, все они являются расширениями RemoteWebDriver'а.

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

Кстати, еще вспомнил один момент — ноды необходимо запускать так, чтобы они могли создать в родном сеансе окно браузера. Именно графическое окно.
В самой статье я постарался описать те разочарования, которые ожидают разработчика, впервые столкнувшегося с подобной задачей. Так что тут вполне могут быть описаны наивные вещи.

Если честно то я не вижу ни одного реальной причины для разочарований.
Различное поведение имплементаций? — Так WebDriver это всего лишь интерфейс для взаимодействия с браузером. Конкретную имплементацию разрабатывают конкретные люди и естественно могут быть разные поведения.
Проблемы с ожиданием? — Так используйте полные возможности а не только implicitlyWait. Кстати Thread.sleep(1000) это просто офигенный костыль к которому можно прибегать только в экстренных случаях.
Проблемы с изменением локаторов в процессе разработки? — Используйте Page Object и другие связанные паттерны и средства.
Мне стало больно скорее от того, что, конкретно в случае с Selenium, возможность «подчистить», в большинстве случаев, сводится к дополнительным шагам, аналогичным по природе тестовым действиям. А такие действия уязвимы для описанных проблем. Т.е. большинство раз тест отработает, но могут случиться исключения. Более того, такие неэффективные методы приносят дополнительные достаточно существенные временные затраты. Вот, видимо, такая ненадежность меня и удивила, во время первого опыта.

Установка предусловий и последующая подчистка через selenium это плохая практика. Нужно стараться от этого максимально уходить.
Именно к этому я и пришел. Большую часть времени разработка проходит в процессе подбора XPath ключей, использовании различных плагинов, файр-багов и обычной JS консоли.

А можно ещё и другими локаторами пользоваться ;) А умение работать с Fire Bug, Chrome Developer Tools, Dragonfly и IE Developer Tools это очень важно и я, честно, не вижу ни какой проблемы в этом.
Selenium 2.0 — сырой продукт


Он не сырой, просто количество поддерживаемых версий браузеров и многочисленные «особенности» самих браузеров никогда не позволят сделать его мега-стабильным «на века».

Тесты Selenium — зависимые тесты


Тут Selenium ни при чем. Любые функциональные и почти всегда интеграционные тесты могут стать зависимыми, если вы не приложите усилий по обеспечению независимости по данным. Есть много техник. Надежнее всего найти ключевой разделитель областей видимости (логин, адрес электронной почты, имя и т.д.), а затем генерировать его уникальным в каждом тесте. Можно пользоваться пулами, вставками наборов данных в БД, вызовами API напрямую и другими способами. Было бы желание…

Тесты Selenium — медленные тесты


Медленные они из-за ваших пауз в коде, за которые надо отрывать руки. А в остальном скорость упирается в скорость работы вашего приложения и в железо. С помощью грида можно очень сильно параллелить тесты и запуск будет очень быстрым. А еще есть SSD, RAM disk, облака с возможностью дешево создать много нод для грида…

Selenium IDE — не помощник


Не лучший инструмент для проверки XPath. Firefinder или Firepath куда лучше.

Получение элемента (findElement)


Тут вообще без комментариев. Вставлять паузы недопустимо. Вы можете воспользоваться explicit wait — это цикл до выполнения условия с минимальными паузами между проверками. Но лупить паузу в секунду на каждый поиск элемента — это жесткая жесть!

Очистка значения поля input


Вы не могли бы опубликовать пример, на котором у вас метод не работает. В идеале как issue для разработчиков WebDriver. Потому что на моем опыте все работает. Альтернативный вариант — использовать JavaScript. Будет быстро, но не так честно.

Firefox may die


В основном эта ошибка встречается у тех, кто не отключает автообновления в Firefox. Версию для тестирования надо выбирать аккуратно. Ну и обязательно проверять версию WebDriver на совместимость. Вы тестируете прежде всего функциональность а не кроссбраузерность. Не забывайте об этом. Для кроссбраузерности можно подобные ошибки перехватывать и повторять тест.

По поводу Thread.sleep(1000);
Я вспомнил с чем связан такой варварский подход.

Суть в том, что это пауза не только для того, чтобы элемент появился в DOM модели и стал доступен для выборки. Суть в том, что элементу еще необходимо успеть отрисоваться, перед тем как с ним начнут далее взаимодействовать (click()). Я пробовал делать перед действием проверку isDisplayed(), но с огромной постоянностью тесты падали на начальном этапе с сообщениями, что элемент не доступен для действия click, так как невидим. Т.е. эта проверка возвращала true еще до того, как элемент полностью отрисуется. Видимо, она возвращает true, как только нет никаких причин считать элемент невидимым с точки зрения значений его стилей, атрибутов, а не визуального отображения, но это догадка.
После поиска альтернативного решения я попробовал все возможные ExpectedConditions, обернутые в WebDriverWait с достаточными таймаутами и периодами, чтобы элемент успел отрисоваться и даже больше. Но по сути это всего лишь обертка над тем же самым isDisplayed. Элемент возвращается неотрисованным.
Делать свою реализацию, которая пытается в таком цикле выполнять действие, пока не добьется успеха, а все исключения ловить и игнорировать — мне показалось тоже грязным вариантом, который при этом не достаточно понятен и читаем, в ситуации если требуется передать код на поддержку третьим лицам.
С другой стороны… хм, можно реализовать WebDriverWait, который не ждет, когда элемент будет отрисован, а просто ждет указанное время — аналогично и взамен Thread.sleep(1000);
Если делать подобные захардкоженые паузы — в принципе плохо, не суть важно будь это спящий поток или цикл с таймингом — дайте плз знать.

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

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

Вы имеете в виду webElement.clear()? или выделение текста и его последующее замещение?
Ок, проверю еще раз оба варианта и выложу результаты, дабы не было заблуждений, но уже завтра :)
Интересно глянуть конкретный пример, где это не срабатывало (упрощенный код страницы и тест). Можно в личку. Просто я очень много раз видел такое, что локатор написан неверно или же постоянно обновляется DOM и элемент устаревает. Обсуждать абстрактного коня в вакууме очень тяжело. Пауза в секунду — не выход в любом случае, потому что завтра секунды может не хватить и что тогда? Ставить 5 секунд? :) Если такая беда случается, то лучше уже кликнуть на элемент из JavaScript или написать кусочек JavaScript, который убедится, что элемент уже доступен. Просто как-то догадка с «прорисовыванием» звучит странно. Другой вопрос, что этот элемент может появляться «с хаками», когда он сначала добавляется в DOM, а потом на него навешивают кучу всего. Надо смотреть.

Тоже самое и с clear(). Выделение с замещением — это известный обходной маневр. Мне не приходилось видеть чтобы clear() не работал. Если примеры будут, то я могу обратиться к разработчикам WebDriver знакомым и показать им.
Прошу прощения что задержался с ответом — был отпуск :)

Сделал демку, webElement.clear() — срабатывает. Попробовал отработать его в собственном проекте — тоже срабатывает.
На данный момент рабочая версия webdriver'а — 2.30.0.
В качестве webElement выступает элемент input с type=«text»

Возможно проблема с этим методом была в версиях, на которых я стартовал разработку тестов — 2.24, 2.25, 2.26, т.к. от этого метода я отказался в начале разработки.

Вообщем странная ситуация, я точно помню что выпадала ошибка и точно помню, что находил на stackoverflow комментарии о проблеме с этим методом. К сожалению в данный момент мало времени, чтобы эксперементировать, но если оно появится — попробую воспроизвести на старых версиях.

Нашел в старых логах сообщение падавшей ошибки:
Caused by: org.openqa.selenium.InvalidElementStateException: Element must not be hidden, disabled or read-only

При этом элемент отображался, был виден и не имел никаких атрибутов, скрывающих его.

Но, повторюсь, лучше проверить на старых версиях
Вероятнее всего был другой элемент, который его перекрывал просто. Такое часто встречается.
Сейчас понял в чем была проблема. Я тогда о ней еще не знал, поэтому не запомнил. Практически гарантированно, что проблема была в несоответствии версии вебдрайвера и версии браузера firefox. Только что опять напоролся, после обновления firefox.
Есть опыт написания тестов под WatiN, ситуация аналогичная. Нестабильно, нужна куча костылей, долго. Плюс только под IE. В какой-то момент коллеги перешли на Selenium, но я так понял, что мало что улучшилось :)
Для Selenium WebDriver существует несколько библиотек-обёрток, в той или иной мере решающие описанные вами проблемы: Thucydides, HtmlElements, Selenide.

Первые две являются фреймворками, заставляющими вас писать структурированные тесты с использованием Page Objects, Step Object и т.п. Я лично советую библиотеку Selenide, т.к. она позволяет писать тесты проще и надёжнее.

См. selenide.org
По-моему TestNG больше подходит для работы в паре с Selenium 2 чем JUnit.
Аргументируйте?
Нормальная возможность настраивать зависимости\последовательности исполнение тестов, группировка тестов. Совсем не спец в тестировании (разработчик), но не так давно мне пришлось сделать небольшое тестирование через UI, где как раз возможности TestNG пригодились (начинал с JUnit, но не хватило функицонала), и это нормально по-моему тк JUnit для классических юнит тестов (независимых друг от друга). Зачем использовать JUnit если у TestNG есть все тоже самое плюс много других плюшек.
Так UI тесты в этмо плане ничем от юнит-тестов не отличаются: все тесты тоже должны быть независимыми.
Во первых зависимость тестов была необходима, так если один тест не прошел (верхнего уровня, допустим переход по меню в определенный сеанс), то и следующие уже выполнятся не должны. Также в моем случае последовательность выполнения тестов тоде была важна. Группировка тестов как бонус.
Лично мои аргументы за TestNG — возможность легко и гибко настраивать группы тестов, а также простота запуска тестов в несколько потоков.
да кстати про распараллеливание тестов забыл
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.