Почему не работает xpath

Функции XPath для динамических XPath в Selenium

Будущих студентов курса «Java QA Automation Engineer» и всех интересующихся приглашаем посмотреть подарочное демо-занятие в формате открытого вебинара.

А также делимся переводом полезной статьи.

В данной статье рассматриваются примеры использования функций XPath для идентификации элементов.

Автоматизация взаимодействия с любым сайтом начинается с корректной идентификации объекта, над которым будет выполняться какая-либо операция. Как нам известно, легче всего идентифицировать элемент по таким атрибутам, как ID, Name, Link, Class, или любому другому уникальному атрибуту, доступному в теге, в котором находится элемент.

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

Чему вы научитесь: [показать]

Обзор функций XPath

Обсудим сценарий, при котором атрибуты недоступны напрямую.

Постановка задачи

Как идентифицировать элемент, если такие локаторы, как ID, Name, Class и Link, недоступны в теге элемента?

Суть проблемы демонстрирует следующий пример:

Как видно из скриншота выше, заголовок «Log in to Twitter» не имеет дополнительных атрибутов. Поэтому мы не можем использовать ни один из локаторов, таких как ID , Class , Link или Name , для идентификации этого элемента.

Плагин Firepath для Firefox сгенерировал следующий путь XPath:

Мы бы не рекомендовали использовать указанный выше путь XPath, поскольку структура страницы или id могут меняться динамически. Если все же использовать этот нестабильный XPath, вероятно, потребуется чаще его обновлять, что подразумевает лишнюю трату времени на поддержку. Это один из случаев, когда мы не можем использовать общее выражение XPath с такими локаторами, как ID, Class, Link или Name.

Решение

Идентификация элемента с помощью функций XPath по тексту

Поскольку у нас есть видимый текст «Log in to Twitter», мы могли бы использовать следующие функции XPath для идентификации уникального элемента.

contains() [по тексту]

starts-with() [по тексту]

Функции XPath , такие как contains() , starts-with() и text() , при использовании с текстом «Log in to Twitter» помогут нам корректно идентифицировать элемент, после чего мы сможем произвести над ним дальнейшие операции.

1. Метод Contains()

Синтаксис. Чтобы найти на веб-странице элемент «Log in to Twitter», воспользуйтесь одним из следующих выражений XPath на основе метода contains() .

Поиск по тексту:

//h1[contains(text(),’ Log in to’)]

//h1[contains(text(),’ in to Twitter’)]

Примечание. Наличие одного совпадающего узла свидетельствует об успешной идентификации веб-элемента.

Из примера выше видно, что методу contains() не требуется весь текст для правильной идентификации элемента. Достаточно даже части текста, но эта часть текста должна быть уникальной. Используя метод contains() , пользователь может легко идентифицировать элемент, даже после смены ориентации страницы.

Обратите внимание, что при указании всего текста «Log in to Twitter» с методом contains() элемент будет также идентифицирован корректно.

2. Метод starts-with()

Синтаксис. Чтобы найти на веб-странице элемент «Log in to Twitter», используйте следующие выражения XPath на основе метода starts-with().

Поиск по тексту:

//h1[starts-with(text(),’Log in to’)]

Из приведенного выше примера видно, что XPath-функции starts-with() требуется по крайней мере первое слово («Log») видимого текста для однозначной идентификации элемента. Функция работает даже с неполным текстом, но он должен как минимум включать первое слово частично видимого текста.

Обратите внимание, что при использовании всего текста «Log in to Twitter» с методом starts-with() элемент будет также идентифицирован корректно.

Недействительный XPath для starts-with() : //h1[starts-with(text(),’in to Twitter’)]

Примечание. Отсутствие совпадающих узлов свидетельствует о том, что элемент на веб-странице не был идентифицирован.

3. Метод text()

Синтаксис. Чтобы найти на веб-странице элемент «Log in to Twitter», воспользуйтесь следующим выражением XPath на основе метода text() .

В этом выражении мы указываем весь текст, содержащийся между открывающим тегом

и закрывающим тегом

Недействительное выражение Xpath для text() :

Идентификация элемента с помощью функций XPath по атрибуту

Мы используем функции XPath ( contains или starts-with ) с атрибутом в тех случаях, когда в теге содержатся уникальные значения атрибутов. Доступ к атрибутам производится с помощью символа « @ ».

Для лучшего понимания рассмотрим следующий пример:

1. Метод Contains()

Синтаксис. Чтобы точно идентифицировать кнопку «I’m Feeling Lucky» («Мне повезет») с помощью XPath-функции contains() , можно указать следующие атрибуты.

Вариант А — поиск по значению атрибута Value

На скриншотах выше видно, что поиск по атрибуту Value слов «Feeling» или «Lucky» с помощью функции contains() позволяет однозначно идентифицировать данный элемент. Стоит отметить, что, даже если мы используем полное содержимое атрибута Value , мы сможем корректно идентифицировать элемент.

Вариант Б — поиск по содержимому атрибута Name

Неправильное использование функции XPath с атрибутом:

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

Если мы воспользуемся атрибутом type при идентификации кнопки «I’m Feeling Lucky», то XPath не сработает.

Наличие двух совпадающих узлов свидетельствует о том, что нам не удалось корректно идентифицировать элемент. В данном случае значение атрибута type не является уникальным.

2. Метод starts-with()

Метод starts-with() в сочетании с атрибутом может пригодиться для поиска элементов, у которых начало атрибута постоянное, а окончание изменяется. Такой метод позволяет работать с объектами, динамически изменяющими значения своих атрибутов. Его также можно использовать для поиска однотипных элементов.

Изучите первое текстовое поле First Name (Имя) и второе текстовое поле Surname (Фамилия) формы авторизации.

Первое текстовое поле First Name идентифицировано.

Второе текстовое поле Surname идентифицировано.

В случае обоих текстовых полей, из которых состоит форма авторизации Facebook, первая часть атрибута id всегда остается неизменной.

First Name

Surname

Это тот случай, когда мы можем использовать атрибут вместе с функцией starts-with() , чтобы получить все элементы такого типа с атрибутом id . Обратите внимание, что мы рассматриваем эти два поля только для примера. На экране может быть больше полей с id , которые начинаются с «u0».

Starts-with() [по атрибуту id]

Важное примечание. Здесь мы использовали двойные кавычки вместо одинарных. Но одиночные кавычки тоже будут работать с методом starts-with .

11 найденных узлов указывают на то, что данное выражение XPath позволило идентифицировать все элементы, id которых начинается с «u0». Вторая часть id («2» для имени, «4» для фамилии и т. д.) позволяет однозначно идентифицировать элемент.

Мы можем использовать атрибут с функцией starts-with там, где нам нужно собрать элементы похожего типа в список и динамически выбрать один из них, передавая аргумент в обобщенный метод, чтобы однозначно идентифицировать элемент.

На примере ниже показано использование функции starts-with .

/ Generic Method /

public void xpathLoc(String identifier)<

//The below step identifies the element “First Name” uniquely when the argument is “2”

E1.sendKeys(“Test1”); / This step enters the value of First Name as “Test 1” /

/ Main Method*/

public static void main(String[] args) <

xpathLoc(“2”); — This step calls the xpathLoc() method to identify the first name.

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

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

Заключение

В этой статье мы рассмотрели, как можно использовать функции XPath contains() , starts-with() и text() с атрибутом и текстом для однозначной идентификации элементов в структуре HTML DOM.

Ниже приведены некоторые замечания касательно функций XPath:

Используйте метод contains() в XPath, если известна часть постоянно видимого текста или атрибута.

Используйте метод starts-with() в XPath, если известна первая часть постоянно видимого текста или атрибута.

Вы также можете использовать методы contains() и starts-with() со всем текстом или полным атрибутом.

Используйте метод text() в XPath, если вам известен весь видимый текст.

Нельзя использовать метод text() с частичным текстом.

Нельзя использовать метод starts-with() , если начальный текст не используется в XPath или если начальный текст постоянно изменяется.

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

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

Об этом расскажем на открытом вебинаре. Регистрируйтесь

Узнать подробнее о курсе «Java QA Automation Engineer» можно здесь.

Источник

Не так страшен XPATH как его незнание

Само собой разумеется и во всех книгах проговаривают, что для нахождения элемента лучше всего и быстрее использовать локаторы id и name и, что характерно, основные примеры по использованию локаторов и по работе Selenium показывают именно с ними. Но в реальной жизни часто бывает так, что id элементов формируется динамически, а потому все привязки к нему бесполезны, class может иметь десятки представителей на странице, а name может отсутствовать. Как вы уже догадываетесь в этом случае нужно применять локаторы xpath и css. В данной статье я не собираюсь говорить о каком то превосходстве над css или сравнивать быстродействие, я лишь расскажу почему я использую именно xpath и как это нужно делать. Букв будет много, так как в свое время мне пришлось достаточно порыться в интернет, чтобы получить нужную мне информацию, я выкладываю все самое полезное, в надежде, что кому это поможет в использовании xpath-локаторов. Важно, что у тебя, мой читатель должно быть хоть небольшой представление о xpath, если его нет, то можешь скачать длинный мануал тут.

Сначала о том, почему новички (и не только) не любят xpath:

  1. Со времен далекой, далекой Галактики, существует миф о том, что xpath во много раз медленнее css, что на данный момент времени не является правдой. Не знаю как обстояло дело раньше, но в наши дни я лично написал несколько тестов с использованием xpath и css и сравнивая их могу сказать, что никакого значительного преимущества нет, а порой даже xpath работает быстрее. Не собираюсь вступать в длительные баталии по поводу скорости, просто разница в несколько миллисекунд нельзя считать значительной, особенно при общей длительности УИ-тестов.
  2. Xpath неверно используют, во многом из-за того, что стандартные панели разработчика и плагины выдергивают xpath из страницы в совершенно непотребном виде, который неудобен и нечитаем. Потому у многих сложилось мнение, что xpath это тяжеловесная и непонятная ерунда.
  3. Нет или по меньшей мере мне не попался какой-нибудь вменяемый мануал по xpath, в основном предлагают ссылки на pdf файл где локаторы приведены всей кучей вместе с css, этакая выжимка, в которой я уверен мало кто разбирается просто из-за обилия информации.

А теперь о том, как обстоят дела на самом деле и в чем преимущества xpath, если его правильно использовать:

— он не уступает (или незначительно уступает) в скорости css

— он понятен и легко читаем, по нему можно понять о каком элементе идет речь

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

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

Итак, несколько правил использования xpath:

  1. Никогда не используй плагины или копирование xpath из кода страницы средствами браузера или веб-разработчика. Вот например как показывает одну ссылку плагин к Файрфокс: //header/div/ul/li[2]/a . Разве из этой ссылки понятно, о каком элементе речь, что мы ищем? Ведь порой бывает, что взглянув на локатор в коде или в тексте исключения мы должны понять о каком элементе речь. Как это можно понять из такой строки? Я уже не говорю о том, что любой код, основанный на таких локаторах упадет при любом дуновении ветерка. Каждый раз, когда ты пишешь локатор подобный//div[1]/div[2]/ul/li (продолжать можно долго) в мире умирает что-то хорошее. Это, если хотите, говнокод тестировщика, который нужно выжигать каленым железом.
  2. Старайся написать xpath как можно короче и понятнее, используй его возможности и схожесть с языком программирования, чтобы и через месяц ты сам мог понять о каком элементе речь и что нужно поправить в случае изменения верстки
  3. Xpath’у время и место! Если есть возможность использовать id, name или попросить разработчиков внести в код id то сделай это!
  4. Вместо длинной цепочки слешей, как указано выше, используй отношения элементов: предок, потомок, сестринский элемент
  5. Можно и нужно использовать логические операции and, not , or
  6. Нормальный xpath всегда начинается с // и не использует фильтры с номером элемента в стиле [2] (например //div[2])

Переходим к делу и практике, тот xpath, что указан выше (//header/div/ul/li[2]/a) на самом деле можно указать в виде //a[text()=’Pricing’]. Согласись, что есть разница и в длине текста и в понимании его, ведь тут видно по тегу, что это ссылка и ее текст –Pricing. То есть ты можешь и сам найти этот элемент на странице визуально и в случае исключения с таким локатором сразу знаешь, что и где искать!

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

  • text() – возвращает текст, содержащийся в элементе. Данную команду незаслуженно забывают и зря, ведь если ты посмотришь на любое веб-приложение, то ты там увидишь кнопки и ссылки, а на кнопках и в ссылках текст. И если id и class у них может меняться, то уверяю, текст на кнопке чаще всего остается тем же, а значит порой правки верстки никак не затрагивают твои локаторы, основанные на тексте! Не стесняйся применять локаторы основанные на тексте! Пример:

Как видим id явно сгенерирован и привязаться к нему нельзя, класс тоже не внушает доверия, кроме того Selenium не разрешает использовать сложносоставные имена в локаторе className, но тут есть текст, который решает проблему: //a[text()=’Contact us’]

  • contains(параметр, искомое) –возвращает элемент если он содержит искомое, знакомая команда не так ли? Ты ее видишь постоянно в языке программирования. Очень удобно использовать в связке с text() если составляем сложный локатор и не знаем точно всего текста, например: //div[@class=’buttons’andcontains(text(),’Save’)] – как видишь, это некоторый элемент, который относится к кнопкам и на нем есть текст Save. Представь, что в твоем тестируемом веб-приложении есть несколько страниц, на которых есть кнопка сохранения, но с разными текстами –сохранить файл, сохранить диаграмму, сохранить отчет и так далее. Тебе не придется писать локаторы для них всех, хватит одного для всех кнопок сохранения и он описан выше. Обрати внимание на использовании в фильтре сразу двух условий!

Кроме того, очень полезная возможность – это искать элемент по одному из слов в названии класса.Пример:

Все решается вот так: //div[contains(@class,’intercomBtn’)] , то есть мы ищем элемент, у которого в классе есть какое-то уникальное сочетание слов. Данная возможность contains очень помогает в самых разных ситуациях! Обрати внимание, что параметр и искомое идут через запятую, нельзя писать contains(text()=’smth’)

  • starts-with(параметр, искомое) –все аналогично contains, только эта команда возвращает элементы начинающиеся с искомого. Если возвращаться к примеру с кнопками сохранения, то локатор можно переписать вот так //div[@class=’buttons’andstarts-with(text(),’Save’)] у нас ничего не упадет, так как слово save обычно первое на кнопке и значит локатор опять же будет работать для всех кнопок сохранения. В общем это более узкая форма contains

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

Формат использования //начальный элемент/отношение::тег(фильтр) конечного элемента. Обрати внимание на два двоеточия после отношения и не забывай после двоеточий указать тег, а лучше и фильтр искомого элемента, так как потомков может быть и много, а нам нужен какой-то конкретный.

  • sibling – возвращает сестринский элемент, попросту говоря элемент, который расположен на том же уровне что и начальный –не потомок и не предок. Бывают двух типов preceding-sibling -сестринский элемент, который расположен до (выше) указанного и following-sibling – сестринский элемент, расположенный после (ниже) указанного. Пример:

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

//div[text()=’Тема’]/preceding-sibling::input — мы сначала находим уникальный элемент с текстом, а потом от него ищем предшествующий сестринский элемент, делая фильтр-уточнение, что ищем именно input. Еще пример:

Нам нужно кликнуть кнопку, на которой нет текста, только иконка, но как видишь у нее все те же проблемы с id плюс есть куча одноименных классов. Нас спасает то, что у предшествующего элемента есть уникальное название класса, вот от него и будем плясать: //div[contains(@class,’listViewMoreActionsButton’)]/following-sibling::div – находим элемент у которого есть уникальное слово в названии класса и от него уже ищем следующий сестринский элемент, с тегом div. Учитывай, что даже если сестринских последующих элементов с тегом div будет много вернется только самый первый!

  • parent иchild, соответственно родитель и наследник(ребенок), обрати внимание что речь идет о непосредственном прямом родителе или наследнике, а не о предке или потомке. Если возвращаться к примеру

То представим, что нам нужен непосредственно элемент с для всех на данной картинке он является родителем (parent) и поэтому его можно вытянуть через любой из них, например //div[text()=’Тема’]/parent::div

Кстати, обращение к родительскому элементу, можно заменить двумя точками и без тега, вот так //div[text()=’Тема’]/..

Так как все элементы в примере — дети, то можно любого из них найти от родителя вот так:

//div[contains(@class,’has-floating’)]/child::input – находим родителя, а от него ищем ребенка с тегом input.

  • descendant – потомок, но отличие от child это может быть потомок любой вложенности, так сказать пра-пра-внук в том числе, а не только сын, потому важно не путать с непосредственным наследником! Пример:

Нам нужна папка именно с определенным именем, но верстка организована так, что сам текст не содержится именно в элементе класса папка, поэтому нам надо найти сначала класс, а потом отфильтровать ту, у которой в потомках есть нужный текст:

//div[@class=’listitem Folder’]/descendant::span[text()=’Folder name’] –сначала находим класс папки, потом среди его потомков ищем тег span и нужный нам текст. Вы можете спросить –а почему просто по тексту не искать? Дело в том, что элементов с таким текстом на странице может быть больше одного, а нам нужна именно папка.

Кстати вместо descendant можно использовать двойной слеш // это означает -любой вложенный элемент. Пример выше превращается в

//div[@class=’listitem Folder’]//span[text()=’Folder name’]

  • ancestor – предок, опять же отличающийся от parent тем, что может быть любой удаленности, то есть прадедушкой. Если возвращаться к предыдущему примеру, то найти элемент папки по тексту можно так //span[text()=’Foldername’]/ancestor::div[@class=’listitemFolder’]

Важно понимать, что можно, но крайне нежелательно использовать в одном локаторе несколько отношений, например:

//div[@class=’One]/ child ::div[@class=’Two’]/ descendant ::input[@class=’Three]. Такой локатор работать будет, но он уже сложно читается и скорее всего есть возможность подобрать другой, не нужно использовать такое без необходимости, помним правило номер 2. Совсем недопустимо использовать в одном локаторе обратные отношения то есть сначала искать потомка, потом его предка или наоборот.

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

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

Источник

Читайте также:  Как настроить сканирование штрих кода
Оцените статью