Selenide: Лаконичные UI тесты на Java

Если я вас правильно понимаю, писать не задумываясь не получится.

В каком-то месте всё равно надо решить, как конкретно проверять, видим ли данный элемент. В идеалогии Selenide это будет примерно так:

$("#my-element1").shoudBe(on);

$("#my-element2").shoudBe(enabled);

$("#my-element3").shoudBe(selected)

 

При вашем подходе, если я правильно понял, condition будет один, но зато разные элементы:

getSelectbox("#my-element1").shoudBe(visible);

getSpanImage("#my-element2").shoudBe(visible);

getRadioSpan("#my-element3").shoudBe(visible)

 

И в том, и в другом случае надо в каждой строке думать, какой элемент или какой Condition использовать. Кода ровно столько же.

А преимущество Selenide я здесь вижу в том, что вам как раз-таки не надо лезть в дебри и расширять базовый класс. Нужно только реализовать самому интерфейс Condition. По-моему, вот это как раз гибко. 

 

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

$("id1").shouldBe(checkboxDisabled);

$("id1").shouldBe(checkboxSelected);

$("id2").shouldBe(tabHeaderDisabled);

$("id2").shouldBe(tabHeaderSelected);

$("id2").shouldBe(tabHeaderMovable);

$("id3").shouldBe(gridRowSelected);

Ну да ладно. На вкус и цвет...

А как же быть с экшенами? Вот простой пример с HTMLEditor:

public void type(String value) {
click();
switch (BROWSER) {
case SAFARI:
executeScript("arguments[0].innerText='" + text + "'", this);
break;
default:
sendKeys(value);
}
}
 
Пока мне видится один не очень элегантный вариант, что-то типа 
$("id1").shoudleBe(HTMLEditorEnabled)
$("id1").shoudleBe(HTMLEditorEditable)
$("id1").typeInHTMLEditor("some text")
 
Хотелось бы увидеть пример тестов, но не синтетических (а-ля gmail, google) - а реального проекта, где написано уже порядка сотни-другой тестов.

Так первый способ обладает ровно теми же недостатками. 

Количество кондишенов может перевалить за сотню?

Ну, это вы загнули. Вы хотите сказать, что в вашем проекте чекбокс рисуется с помощью span и img сотней разных способов? Не верю.

А если так, то вам стоит поговорить с программерами, чтобы они сделали это как-нибудь попроще. Пусть у всех чекнутых чекбоксов будет класс "checked", как бы они там ни отрисовывались. Тестер при тестировании приложения должен думать о бизнес-логике, а не о том, какой из 100 способов отрисовки чекбокса выбрать для каждого элемента. 

 

 

С экщенами просто: сделайте свой static метод public static void type(String cssSelector, String value) {... всё, что вы написали ...}

и вызывайте его откуда угодно и когда угодно. Не надо лезть ни в какие дебри. 

 

Пример тестов из реального проекта вам никто никогда не покажет, ишь чего захотели. Это же секретная информация. 

К счастью, один наш клиент разрешил показать их код на конференции. Видео есть в блоге Selenide: http://ru.selenide.org/2013/05/09/video-selenide-on-seleniumcamp/

Там, конечно, всего проекта не видно, только чуть-чуть тестов, но зато они вполне реальные. 

Ну вот, записал в Wiki:

https://github.com/codeborne/selenide/wiki/How-Selenide-creates-WebDriver

 

1 лайк

Не вижу этого раздела у вас в оглавлении:

https://github.com/codeborne/selenide/wiki

Он доступен только по прямой ссылке?

Точно. Спасибо, добавил ссылку в оглавление.

Хотя мне до сих пор непонятно, нафига нужен RemoteWebDriver. По той же причине, что п. 1.

RemoteWebDriver+Grid в первую очередь нужен для масштабирования  выполнения тестов в рамках одного браузера. Исходя из моего опыта, среднее время выполнения теста составляет 1-2 минуты. Т.е. уже при наборе тестов в количестве около сотни придется задуматься о параллелизации. Добавим сюда мульти[браузерное/серверное] тестирование и станет ясно, что альтернатив этой связке нету.

Например:

Имеем пять тестовых машин и набор тестов, которые идут в:

IE - 30 min

FF - 20 min

Chrome - 15 min

В случае "по-браузерного" запуска репорт будет получен через 30 минут. В случае использования грида - через 6 минут.

Не надо лезть ни в какие дебри. 

Эмм, ну как же убедить что придется? Ну хотя бы потому, что в коде "косяков" вагон и маленькая тележка.

private void followLink(WebElement link) {
String href = link.getAttribute("href");
link.click();

// JavaScript $.click() doesn't take effect for <a href>
if (href != null) {
open(href);
}
}

open после click - это нормально?

 

protected void fireEvent(final String event) {

причем тут document.activeElement?

 

} catch (WebDriverException elementDoesNotExist) {
return false;

т.е. в случае краша браузера/не валидного локатора и прочих нештатных ситуаций exists будет послушно возвращать false, умалчивая о причинах, а всяческие wait'ы - ждать.

 

private static void waitUntilAlertDisappears() {

это дед-код - ничего он ждать не будет

 

 ieCapabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true);

так делать очень плохо

 

By.xpath(".//*[contains(normalize-space(text()), \"" + elementText + "\")]")

не совсем корректный xpath

и т.д.

Ну, это вы загнули. Вы хотите сказать, что в вашем проекте чекбокс рисуется с помощью span и img сотней разных способов? Не верю.

Сотней - нет. С пяток вариантов насчитать могу.

А если так, то вам стоит поговорить с программерами, чтобы они сделали это как-нибудь попроще.

Угу. Они с радостью перелопатят весь код проекта, которому уже за пять лет перевалило, и попросят задание посложнее :)

 

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

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

Например, Gradle умеет сам запускать тесты в нескольких параллельных процессах. Это на одной машине. А если хочется запустить тесты на нескольких машинах, то проще запустить их на Jenkins с несколькими нодами. 

Поговорив с народом, я понял, что Selenium Grid просто альтернатива этому варианту. Для тех, кто с Jenkins незнаком, а с Grid знаком, вполне себе вариант.

 

По "косякам":

1. followLink: да, open после click вполне нормально. Там же в комментарии вроде понятно написано, что в определённых услоиях просто click на элементе не срабатывает, и тогда мы и делаем open. Не нужно - не используй.

 

2. fireEvent: document.activeElement - это элемент, на котором в данный момент находится фокус. И на этом элементе нам нужно сгенерировать событие onChange или onClick. Что смущает?

3. exists() - да, функция возвращает false в случае любых крэшей, в этом и есть её предназначение. Её не нужно использовать во всяческих wait'ах. Для ожидания есть другия функции - например, $("input").should(exist) - вот она-то подождёт и сообщит о причинах.

4. waitUntilAlertDisappears: ну я не знаю, у меня используется в реальном проекте и вполне себе работает. Как только alert пропадает, вываливается NoAlertPresentExceptionА что с ним не так, я не очень понял?

5. Почему INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS - плохо? Я наоборот, нашёл в интернете дофига советов его использовать.

6. Про xpath не понял, почему он некорректный. Работает же. 

 

Внутри Selenide использован механизм Reflection API, который действительно нетривиальный. Нужен он был для того, чтобы создать объект WebElement с несколькими дополнительными методами. Туда просто не надо лезть. Вы же не лезете внутрь своего телефона, вы его просто используете. 

 

"По сути весь код может вполне себе уместиться в пару тройку классов." - ну напишите, с удовольствием посмотрим. :) 

Видимо не до конца понимаете. Упрощу пример:

Дано:

1. N тестовых машины

2. Набор из 50 тестов

Надо:

1. Запустить все тесты параллельно в IE

2. На каждой машине в один момент времени может быть запущено не более одного экземпляра браузера

 

Решение разбить 50 тестов на N блоков и параллельно их запустить не вариант:

1. Не будет цельного тест репорта.

2. Тесты могут быть зависимы друг от друга.

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

4. Падение тестовой машины (BSOD/LAN problem etc.) приведет к скипу всего блока

 

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

 

1. followLink: не разу не сталкивался с тем, что какой-либо драйвер не мог кликнуть по линку с href. Если вы столкнулись с проблемным линком, на котором клик не проходил - то уверяю вас, это было не правило, а исключение. Чем черевато тут open: во-первых - двойная загрузка страницы [click - waiting - open - waiting]; во-вторых - в href попросту может оказаться about:blank, а нужный код будет в onclick; в-третьих - функционал клика - что если клик по линку удаляет, например, документ - приложение после такого клика, за open по голове не погладит. 

2. fireEvent. Я вызываю у некого SelenideElement метод fireEvent, что я ожидаю? Что у этого элемента фаернится искомый эвент. А по факту эвент пройдет у неизвестно какого activeElement'а. А document.activeElement'ом может быть <img>,<div>,<span> и т.д.? Судя по описанию - врядли https://developer.mozilla.org/en-US/docs/Web/API/document.activeElement.

3. Идем в should, видим waitUntil. Идем в waitUntil, видим все тот же catch (WebDriverException ignore) {}. Зачем ждать: если локатор не валидный - он сам не поправится, если браузер упал - он сам не поднимется?

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

5. Плохие советы. http://jimevansmusic.blogspot.com/2012/08/youre-doing-it-wrong-protected-mode-and.html. Да и в javadoc написано

public static final java.lang.String INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS
Capability that defines to ignore ot not browser protected mode settings during starting by IEDriverServer. Setting this capability will make your tests unstable and hard to debug.

6. Работает до тех пор, пока не встретится элемент, типа <div>   <span></span> div_text </div>

Про Selenium Grid теперь многое стало понятно. Спасибо. 

Многие пункты спорные (например, с какой это стати на одной машине может быть не больше одного браузера одновременно? Конечно может быть и несколько!), но в целом идея понятна. 

1. С followLink ситуация простая: не нужно - не используй. Я даже, пожалуй, удалил бы этот метод, но теперь нельзя: вдруг кто использует.

Есть старый добрый click(), если он делает то, что надо - используй его. В чём проблема-то?

2. fireEvent: этот метод - ПРИВАТНЫЙ! Проблемы как таковой нет: вы не вызовете его на любом элементе. Говорю же, не надо лезь в дебри.

3. Тем не менее, если пропал браузер, или случилась ещё какая-то беда, в итоговом сообщении об ошибке это будет отражено.

4. Да, может быть, и вправду не нужно этого ждать. Надо будет мне поиграться и если удалить этот код, если действительно так. Но если это так, то этот код и ничего плохого тоже не делает. 

5. Спасибо за наводку, уберу эту настройку в следующей версии.

6. А почему?

например, с какой это стати на одной машине может быть не больше одного браузера одновременно? Конечно может быть и несколько!

Может - но смотря каких, и смотря в какие количествах :)

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

А для IE это аксиома, даже дефолтовые настройки грида - это 5xFF, 5xChrome, 1xIE: http://code.google.com/p/selenium/wiki/InternetExplorerDriver 

Вот браузеров с синтетическими эвентами (Chrome, Opera) можно запускать сколько угодно, причем они не будут мешать нативным.. Но ресурсы же не резиновые, а тестовые машины, в большинстве своем, это VM, которые не вытянут и дефолтовый набор браузеров грида.
 

3. Будет. Но будет и "холостой" explicit wait.

6. По спецификации: http://www.w3.org/TR/xpath/#node-tests. text() - в действительности это не текст элемента, а функция, которая возвращает все текстовые ноды данного элемента. Т.е. в случае <div>  <span></span> div_text </div> имеется две текстовых ноды: //div/text()[1] == '   ',  //div/text()[2] == ' div_text '; а //div[contains(text(),'div_text')] ==//div[contains(text()[1],' div_text ')] == NoSuchElement

1 лайк

2 сообщения перенесены в новую тему: Можно ли изменить дефолтный браузер в самой конфигурации Selenide?

8 сообщений перенесены в новую тему: Selenide умеет перехватывать стандартные алерты авторизации?

5 сообщений перенесены в новую тему: Можно ли отменить ожидание загрузки страницы в selenide?

4 сообщения перенесены в новую тему: Где найти полную документацию по selenide?

5 сообщений перенесены в новую тему: Простой тест на Selenide не запускается, что я делаю не так?

9 сообщений перенесены в новую тему: Как добавить свои листенеры к Selenide коду?

Сообщение перенесено в новую тему: Почему я получаю NoSuchElementException в моем PageObject c selenide?

Сообщение перенесено в новую тему: А идентификация элементов в Selenide используется “на месте”?