XPATH, вверх и вниз по DOMу

xpath

Итак, мы посмотрели как писать локаторы XPATH, как писать CSS, договорились не копировать локаторы слепо и по возможности писать кратко и понятно.

Напоминаю алгоритм написания любого локатора:
1) Если есть, то используем уникальный id или имя класса
2) Если не выполняется пункт 1, то идем к разработчикам и просим его обеспечить
3) Если не выполняется пункт 2, то пытаемся использовать CSS, если можно написать коротко
4) Keep calm and use XPATH

Сегодня я покажу написание локаторов на небольшом и конечно искусственном примере, который показывает, что не всегда можно использовать CSS или другие локаторы. Перед нами сайт дорого во всех смыслах Ростелеком, с предложениями по интернет: https://spb.rt.ru/packages/tariffs

В тестах нам нужно нажать кнопку «Подключить» у тарифа с ценой 1010 рублей. Но мы при этом знаем, что тарифы часто меняются и данный тариф с его кнопкой может быть расположен в любом месте страницы, с любым id. (На самом деле задача реальная, но любые совпадения случайны)
1
Исходя из условий, мы не можем просто получить список кнопок с надписью «Подключить» и кликнуть вторую, в любой момент она может стать третьей. Мы также не можем быть уверены, что id тарифа не изменится, кроме того, было бы круто иметь один общий локатор, который бы нашел кнопку «Подключить» у тарифа по его цене. Класс также не уникален и возвращает список элементов.

2

Что же есть уникальное, к чему можно прицепиться? Цена тарифа! У всех тарифов разные цены, причем в данном случае мы как раз ищем цену 1010 рублей.3

У элемента цены есть уникальный атрибут data-fee=’1010′ по которому мы можем точно найти его, причем только его одного.

//*[@data-fee=’1010′]  — мы точно находим нужную цену. Теперь нужно двигаться к кнопке Подключить по дереву DOM и тут есть два варианта:

1) подняться от цены к ее родителю (classtariff-desc__cost_m-cell«), от него получить следующий сестринский элемент (classtariff-desc__cost_l-cell«) и уже в нем получить кнопку. Получается один шаг вверх и два вниз

4

//*[@data-fee=’1010′]/.. — переходим к родителю

//*[@data-fee=’1010′]/../following-sibling::div — уходим вниз к сестринскому элементу

//*[@data-fee=’1010′]/../following-sibling::div/a — получаем нашу кнопку

2) так как кнопка Подключить и цена входят в состав одного блока, то гораздо лучше от цены найти ее предка (не непосредственного родителя!), который является этим блоком (classtariff-desc__cost tariff-desc__cost_reset js-price-blocks«) у него просто взять вложенный элемент кнопки. Получается всего два шага -вверх и вниз

5

//*[@data-fee=’1010′]/ancestor::div[contains(@class,’js-price’)] — получаем предка(ancestor), с классом содержащим js-price

//*[@data-fee=’1010′]/ancestor::div[contains(@class,’js-price’)]//a — у нашего элемента просто ищем вложенную ссылку (нашу кнопку).

Можете убедиться, что оба локатора являются уникальными, и меняя цену, можно получать локаторы кнопки у всех остальных тарифов по их цене.

Я чаще всего работаю с элементами во вкладке Элементы инструментов разработчика браузера, чтобы видеть соседние элементы, предков и вообще дерево DOM. Но иногда удобнее проверять локаторы во вкладке Консоль, набрав команду вида $x(«выражение») , там видно только конкретно найденные элементы

6

Надеюсь вам никогда не придется идти в алгоритме поиска элемента дальше 1 пункта.

Software-Testing.Ru

Реклама

Сравниваем скорости поиска элементов

cross

Я уже не раз писал, как и многие до меня, что несмотря на распространенное мнение поиск с помощью XPATH  не медленнее поиска с помощью CSS локаторов. Выдалась свободная минутка и решил потестить немного скорость поиска элемента с помощью разных локаторов и в разных браузерах: Chrome, Firefox, InternetExplorer. Естественно, замеры относительны и воспроизводимы с теми же цифрами только на моей машине, однако есть интересные тенденции.

В Python есть удобная утилита timeit в стандартной библиотеке, к сожалению не смог найти что-то похожее на Java, поэтому просто накидал метод который 100 раз подряд находит элемент по заданному  локатору и выдает среднее затраченное время.

Поиск проводил на странице https://google.ru  и по идее искал я один и тот же элемент, просто задавал различные локаторы для него. Время в миллисекундах.

Браузер

XPATH

CSS

ID

CLASS

NAME

‘//form[@class=’tsf’]’

‘form.tsf’

‘tsf’

‘tsf’

‘f’

Chrome 6-7 6-7 7 6 6-7
Firefox 3-5 4-5 3-5 3-4 3-4
IE 50 (!) 78(!!) 77-80 77-79 76-79

Я конечно знал, что Chrome быстрый, в последнее время я работаю только с ним, но не ожидал, что Internet Explorer будет настолько медленнее, искренне сочувствую тем, кому приходится тестировать УИ на нем!

Второй неожиданностью стало то, что Firefox пусть и немного, но побыстрее Chrome.

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

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

Не успокоившись на этом, запустил консоль Python, который сейчас активно осваиваю, и воспользовался утилитой timeit, которая выдает среднее время за указанное количество повторов функции. Локаторы и сайт естественно остались теми же. Цифры были уже другие, но закономерности сохранились, а именно: Firefox немного быстрее Chrome, Internet Explorer на порядок медленнее обоих браузеров, но при этом поиск по XPATH у него работает гораздо бодрее всех остальных.

Итак, выводы:

1) поиск по XPATH не быстрее CSS, а для браузера Internet Explorer даже наоборот, есть смысл все локаторы писать только на XPATH!

2) Chrome не самый быстрый браузер, пусть и совсем чуть-чуть, но Firefox ищет побыстрее, видимо обновления пошли на пользу Лисе

3) Internet Explorer must die!

4) поиск по id или class имеет только преимущество в удобстве написания локатора, если есть уникальные id или имя класса. Вопреки некоторым встреченным в интернете рассказам, поиск по id или class не быстрее остальных видов поиска

Вот тут пожно почитать про xpath, а вот тут про css!

Software-Testing.Ru

Еще немного о WebDriver

item1313068_600px

Решил добавить еще немного информации, о которой не все, как мне кажется, знают и не на всяком курсе рассказывают. Кому-то может показаться любопытным:

securityservСуществует 2 способа паузы, реализуемой через Thread.sleep() непосредственно в библиотеке вебдрайвера. Кроме описанного вот тут, есть еще интерфейс Sleeper, правда все его удобство кончается на возможности задания точного периода ожидания, от необходимости обрабатывать InterruptedException вы не избавлены!

Sleeper.SYSTEM_SLEEPER.sleep(new Duration(3,TimeUnit.SECONDS));

Это чисто для ознакомления, надеюсь вы не будете это использовать в коде тестов для ожидания элементов. Лично мне не ясно для чего такое дублирование кода в библиотеке и почему в Actions при вызове pause() не вызывать Sleeper.

securityservЕсть специальный класс для безопасного использования вебдрайвера в многопоточном тестировании, который рекомендуется для любого многопоточного использования.

webDriver = ThreadGuard.protect(webDriver);

На данный момент могу лишь сказать, что не заметил разницы в скорости выполнения тестов, в сравнении с простым драйвером, потому можно рекомендовать для использования.

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

То есть, это скорее способ дополнительно гарантировать себе, что драйвер используется безопасно, как только видите исключение в стиле

Exception in thread "Thread-0" org.openqa.selenium.WebDriverException: 
Thread safety error; this instance of WebDriver was constructed on thread main (id 1) and is being accessed by thread Thread-0 (id 67)
This is not permitted and *will* cause undefined behaviour

нужно принимать меры.

securityservВ библиотеке WebDriver есть свой класс для работы с файлами и директориями FileHandler. Его возможности вы можете посмотреть в документации, а я приведу лишь один пример. Скажем для создания скриншота в основном используют класс из google-guava или apache-commons, но это можно сделать и так, не используя сторонних библиотек

public void make(String fileName) {
  File scrFile = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE)
     try {
       new FileHandler().copy(scrFile, new File(fileName));
     } catch (IOException e) {
        //логируем и т.д.
     }
}

securityservКроме всем известных способов задания метода поиска By, есть и методы работающие сразу с набором элементов — ByAll и ByChained.

Применяя класс ByAll, мы собираем все элементы, которые соответствуют указанным локаторам. Внимание, будут найдены все элементы, удовлетворяющие каждому из локаторов, а не им всем. Это может быть удобно, если нам нужен список различных элементов: к примеру(ну так случилось!) у вас на странице много кнопок, но локаторы у них разные. С помощью этого класса, можно получить список всех кнопок, перечислив все локаторы.

driver.findElements(new ByAll(By.id("a"), By.id("b")));

— вернет список элементов, в котором будут все, соответствующие локатору id=’a’, а кроме того все, соответствующие локатору id=’b’. То есть это аналогично результату, если бы мы нашли список элементов id=’a’, нашли список id=’b’ и смержили два списка в один.

Второй класс ByChained действует по-другому – он возвращает все элементы последнего локатора, которые при этом расположены внутри элементов с начальными локаторами, то есть являются их потомками. Может быть полезен, если нужны потомки какого-то класса, скажем элементы выпадающего списка.

driver.findElements(new ByChained(By.id("a"), By.id("b")));

— вернет список элементов с локатором id=’b’ (!), которые при этом потомки (вложены) в элемент(ы) с локатором id=’a’.

Если в первом варианте (ByAll) мы можем спокойно перепутать локаторы местами – все равно вернет все элементы, то во втором случае (ByChained) нужно быть точным в порядке локаторов, иначе вернется пустой список.

securityservЛюбителю ОС Windows будет интересно узнать, что в библиотеке есть класс с многозначительным названием WindowsUtils. Как и большинство (все?) утилитных классов он содержит статичные методы, нет необходимости создавать объект класса. Умеет он не так уж много, из полезного можно определить является ли текущая ОС действительно виндой

WindowsUtils.thisIsWindows();  // возвращает true рабам Микрософт :-)

И если работаем именно в Окнах, то можно, к примеру, убить процесс по его имени или id. Думаю все в курсе, что в случае падения теста по исключению (не ассерту) или если забыли вызвать driver.quit()  — процесс драйвера продолжает висеть в работающих, даже если закрыть браузер. С помощью утилит, его можно убить, скажем перед началом тестов (чтобы убедиться, что нет в процессах никаких висящих драйверов)

WindowsUtils.killByName("chromedriver.exe"); //убивает хромдрайвер, если он есть в процессах

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

 

Вот тут можно почитать о других не самых известных фишках Selenium

Software-Testing.Ru

Чтение документации как средство от граблей и костылей.

se

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

securityservПоиск элемента по тексту при помощи CSS. У меня за время использования Selenium скопилась целая библиотека книг, интересных статей и видео о использовании WebDriver и во многих из них, причем достаточно свежих, упоминается CSS локатор в стиле a:contains(‘text’) . То есть люди переписывают этот локатор, даже не проверяя, работает ли он. Но данный локатор не работает! В спецификации W3C можно увидеть пункт с довольно зловещим номером.

666

Особенно мне понравилось недавно просмотренное видео, где хвалили один из новомодных курсов по автоматизации, где радостный участник рассказал, что его научили применению локаторов,  в числе прочего использовать и такой локатор.

securityservБудем честны, хоть раз каждый из нас использовал паузу в стиле Thread.sleep(), а кто не признается, тот и сейчас пользуется 🙂 Как я уже писал – это плохой способ ждать чего-либо и не стоит этим злоупотреблять в своем коде, но если уж решили использовать, то не нужно писать развесистый куст с Thread.sleep() и перехватом InterruptedException. Библиотека WebDriver содержит возможность подождать, впрочем внутри там тот же самый sleep()

new Actions(driver).pause(1000).perform();

— так короче и не бросается в глаза использование слипов.

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

securityservWebElement не хранит свойств элемента страницы. Работая с Java, мы привыкли, что полноценный, сформированный объект хранит в себе свои поля, предоставляет публичные геттеры и не зависит от других объектов. Скажем ArrayList со значениями, полученными откуда угодно, доступен независимо от текущей доступности источника данных. Но WebElement  для запроса тега, текста или других параметров использует запрос к вебдрайверу и если браузер закрыт или изменилась страница, был переход на другой УРЛ, то падает исключение.

Пример:

WebElement element = driver.findElement(By.id(“one”));
driver.get(“http://anotherPage.com”);
System.out.println(element.getText()); // тут падает StaleElementReferenceException так как элемента нет на странице.

Казалось бы, объект element создан и все знает о своем состоянии, но по факту это не так, любые параметры получаются запросом к драйверу. И в коде

if (element.isDisplayed()) {
    System.out.println(element.isDisplayed());
}

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

securityservУ вебдрайвера есть АПИ работы с логами браузера, что порой очень удобно – можно после каких-то действий проверить нет ли ошибок в консоли. Но работает это апи довольно неочевидным образом – при запросе логов они удаляются (для запрошенного уровня browser, driver и так далее) и заново их получить (те же самые логи) вы не можете!

То есть допустим два простых метода получения ошибок браузера и проверки наличия таких ошибок

public List consoleErrors() {
        return driver.manage().logs().get("browser").filter(Level.SEVERE);
    }

public boolean isConsoleErrorsExists(){
        return consoleErrors().size() > 0;
}

Если мы воспользуемся методом isConsoleErrorsExists() то даже если ошибки и были, мы их уже не увидим – логов больше нет, мы их запросили внутри метода, вызвав consoleErrors(). Используйте логи сразу при получении, второго шанса не будет!

securityservПри использовании поиска по локатору className будут найдены не только все элементы у которых класс точно равен искомому, но и все те, где искомое лишь один из классов элемента. То есть при поиске By.className(«link») будут найдены и элементы, где class=’link’, и все те, кто содержит link, например class = ‘link b-sethome__link i-bem link_js_inited’. Это нужно учитывать.

securityservИногда нужно проверять какие то ссылки со страницы, или новые урл на предмет доступности, я в свое время накидал простенький код для проверок с использованием средств Java. Но в библиотеке Selenium уже есть очень полезный в подобных случаях класс UrlChecker, позволяющий проверять доступность ссылок, а конкретнее он ждет в течение заданного времени, что УРЛ вернет код ответа 200, означающий доступность страницы. Это можно сделать так

 new UrlChecker().waitUntilAvailable(timeOutInSeconds, TimeUnit.SECONDS, new URL(“http://site.com”));
// останется только обработать TimeOutException

securityservКак то приходилось писать костылек по скроллированию большой интерактивной страницы в самый низ для проверок, но оказалось и тут все написано до нас! Есть очень удобный интерфейс Locatable, который позволяет получить координаты элемента относительно окна, страницы или всего экрана. А самое важное, что для этого происходит автоматически скроллирование к этому элементу, чтобы он стал видимым. Применяется вот так

((Locatable) element).getCoordinates().inViewPort();

Магия происходит именно в методе inViewPort(); согласно спецификации вместо него с тем же эффектом можно вызвать и onScreen() – но он пока не реализован в драйверах, в отличие от inViewPort().

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

И всех с прошедшими праздниками!

Software-Testing.Ru

Отладка Selenium тестов в jshell, не забывая про Idea

java9

Многие уже в курсе, что с Java 9 к нам пришла и джава-консоль jshell, позволяющая быстро выполнять джава-код без лишних телодвижений. Например для извечного «Hello, world!» не нужно создавать класс, а в нем метод main, просто пишем в консоли System.out.println(«Hello, world!»).

Jshell как уже говорилось идет в составе 9 Java, при условии верно прописанных переменных окружения просто пишем в командной строке jshell  и немного ждем, запуск не мгновенный.

По умолчанию в jshell импортировано несколько пакетов, которые можно посмотреть командой /imports, все остальное при необходимости нужно импортировать самим.

imp

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

file

Но ближе к делу! Консоль можно использовать для отладки ваших тестов на  Selenium потому что она позволяет:

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

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

jshell —class-path C:\drivers\selenium-server-standalone-3.8.1.jar

path

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

chrome

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

Завершив, выводим весь наш сценарий

list

И на сладкое! Еще удобнее работать с jshell в среде разработки Intellij Idea! Просто открываем терминал Идеи, запускаем там jshell с нужными настройками и готово. Тут есть большое удобство в том, что можно тут же код копировать из Идеи в консоль и обратно.

idea

Не забывайте почитать документацию по jshell, там много интересного.

P.S. Я не предлагаю вам отлаживать или писать тесты именно так, скорее продемонстрировал еще одну возможность, которая, на мой взгляд, может быть удобна в определенных ситуациях.


Software-Testing.Ru

Headless Chrome вместе с Selenium WebDriver — для нетерпеливых

google_now_in_chrome_canary_release_nemmfq

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

Естественно, кроме самого браузера необходимо дождаться и новой версии chrome driver (текущая 2.29). Но если ждать не хочется, а хочется уже сейчас посмотреть и попробовать, то вот простой рецепт (проверялось для chrome driver 2.29 на Windows 10)

Скачиваем и устанавливаем Chrome Canary, который поддерживает все новые функции будущей версии Хрома. Не беспокойтесь, установка идет в отдельную папку по умолчанию и ваш родной текущий Хром браузер никак не пострадает. Сразу запоминаем или копируем путь установки.

Стандартно указываем путь к нашему драйверу хром

System.setProperty("webdriver.chrome.driver", System.getProperty("user.dir") + "/vendors/chromedriver.exe");

в данном случае, у меня драйвер лежит в папке проекта, в директории vendors.

Далее указываем хром проперти для использования headless режима

ChromeOptions options = new ChromeOptions();
options.setBinary("C:\\Users\\admin\\AppData\\Local\\Google\\Chrome SxS\\Application\\chrome.exe");
options.addArguments("--headless");

в методе setBinary мы указываем путь к расположению нашего Chrome Canary, ну и гвоздем программы устанавливаем аргумент —headless, который говорит сам за себя.

далее, опять же по стандарту, просто создаем объект браузера

driver = new ChromeDriver(options);

Можно запускать!

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

options.addArguments("window-size=1800x900");

где 1800х900 это размер, нужный вам.

Родные методы driver.manage().window().maximize(); или driver.manage().window().setSize(); тут не сработают, так как chrome driver все еще 2.29  и видимо пока не может использовать эти операции с headless браузером.

Других сбоев пока не заметил, все кликается, текст вводится, скриншоты выполняются без проблем. Так что кому не терпится — можно пробовать и экспериментировать.

Software-Testing.Ru

Плагины браузера в помощь тестеру

аа

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

Chrome:

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

scr

  • Clear Cache. Обновляет вкладку со сбросам кэша (период настраивается), что бывает очень удобно, особенно для приложений, которые хранят много данных в локальном хранилище. То есть чтобы провести тест «с нуля» порой нужно сбросить кэш браузера, а лезть для этого в настройки не хочется.

cach

  • Spell Checker. Как понятно из названия -проверяет правописание на нескольких языках, не так удобен как плагин у Firefox о котором расскажу ниже. В Хроме требуется скопировать нужный текст и вставить в окно плагина для проверки, что не всегда удобно и просто.

spell

  • Quick Language Switcher. Позволяет изменить язык по умолчанию в браузере, что удобно для проверки локалей. Многие приложения, и наше не исключение, определяют язык пользователя по умолчанию и отрисовывают страницу в соответствии с ним. То есть человек из России откроет сайт на языке родных осин, а англичанин на языке Шекспира. Ну а тестеру надо иногда проверять что все это верно работает.

lang

  • What Font и ColorZilla. Плагины для проверки оформления. Первый, как видно по названию, показывает название шрифта при наведении на него. Второй показывает точный цвет любого элемента как в формате RGB так и шестнадцетеричном. Использую редко, но порой бывает, что прямо в требованиях описан конкретный шрифт или цвет.

font

color

Mozilla Firefox

  • Firebug. Отличный плагин, достойно заменяющий панель разработчика, с огромным количеством функций. В принципе аналог панели разработчика в Хроме, но на мой взгляд, в плане анализа элементов верстки гораздо удобнее. http://getfirebug.com/

fire

  • Firepath. Плагин, который работает непосредственно с Firebug и позволяет находить локаторы CSS и Xpath подсвечивая их на странице или в верстке, проверяет синтаксис введенных выражений.

path

measure

add

На этом пожалуй все, я указал только плагины, которыми пользуюсь сам, ваши могут отличаться, дело вкуса. К примеру проверка xpath для хрома в плагине реализована не так удобно, как в ФФ, элементы верстки мне тоже удобнее смотреть в Firebug, но вот другие функции панели разработчика лучше реализованы в Хроме.

Software-Testing.Ru