Селекторы CSS работают намного лучше, чем Xpath, и это хорошо задокументировано в сообществе Selenium. Вот несколько причин,
- Механизмы Xpath различны в каждом браузере, поэтому делают их несовместимыми
- IE не имеет собственного механизма xpath, поэтому selenium внедряет свой собственный механизм xpath для совместимости своего API. Следовательно, мы теряем преимущество использования встроенных функций браузера, которые по своей сути продвигает WebDriver.
- Xpath имеет тенденцию становиться сложным и поэтому, на мой взгляд, его трудно читать
Однако есть некоторые ситуации, когда вам нужно использовать xpath, например, для поиска родительского элемента или поиска элемента по его тексту (я бы не рекомендовал позже).
Вы можете прочитать блог Саймона здесь . Он также рекомендует CSS вместо Xpath.
Если вы тестируете содержимое, не используйте селекторы, зависящие от содержимого элементов. Это будет кошмар обслуживания для любого региона. Попробуйте поговорить с разработчиками и использовать методы, которые они использовали для экстернализации текста в приложении, такие как словари или пакеты ресурсов и т. Д. Вот мой блог, в котором это подробно объясняется.
редактировать 1
Благодаря @parishodak вот ссылка, которая предоставляет цифры, доказывающие, что производительность CSS лучше
Я собираюсь поддержать непопулярное мнение тегов SO selenium о том, что XPath предпочтительнее CSS в долгосрочной перспективе.
Этот длинный пост состоит из двух разделов - сначала я сделаю доказательство того, что разница в производительности между ними составляет 0,1-0,3 миллисекунды (да, это 100 микросекунд ) , а затем я поделюсь своим мнением, почему XPath более мощный.
Разница в производительности
Давайте сначала займемся «слоном в комнате» - этот xpath медленнее, чем css.
С текущей мощностью процессора (читайте: все, что x86 произведено с 2013 года) , даже на виртуальных машинах browserstack / saucelabs / aws, и с развитием браузеров (читайте: все популярные за последние 5 лет) это вряд ли имеет место. Разработаны движки браузера, поддержка xpath едина, IE не используется (надеюсь, для большинства из нас) . Это сравнение в другом ответе цитируется повсюду, но оно очень контекстно - сколько из них запускают или заботятся об автоматизации против IE8?
Если есть разница, то она составляет доли миллисекунды .
Тем не менее, большинство высокоуровневых фреймворков в любом случае добавляют по крайней мере 1 мс накладных расходов на вызов необработанного селена (оболочки, обработчики, сохранение состояния и т. Д.); мое личное оружие - RobotFramework - добавляет минимум 2 мс, и я более чем счастлив пожертвовать тем, что он дает. Обратный путь от AWS us-east-1 до концентратора BrowserStack обычно составляет 11 миллисекунд .
Таким образом, с удаленными браузерами, если есть разница между xpath и css, она затмевается всем остальным по порядку величины.
Измерения
Публичных сравнений не так уж и много (я действительно видел только цитируемое) , так что вот грубое одноразовое, фиктивное и простое.
Он найдет элемент по двум стратегиям X раз и сравнит среднее время для этого.
Цель - целевая страница BrowserStack и ее кнопка «Зарегистрироваться»; снимок экрана html при написании этого сообщения:
Вот тестовый код (python):
from selenium import webdriver import timeit if __name__ == '__main__': xpath_locator = '//div[@class="button-section col-xs-12 row"]' css_locator = 'div.button-section.col-xs-12.row' repetitions = 1000 driver = webdriver.Chrome() driver.get('https://www.browserstack.com/') css_time = timeit.timeit("driver.find_element_by_css_selector(css_locator)", number=repetitions, globals=globals()) xpath_time = timeit.timeit('driver.find_element_by_xpath(xpath_locator)', number=repetitions, globals=globals()) driver.quit() print("css total time {} repeats: {:.2f}s, per find: {:.2f}ms". format(repetitions, css_time, (css_time/repetitions)*1000)) print("xpath total time for {} repeats: {:.2f}s, per find: {:.2f}ms". format(repetitions, xpath_time, (xpath_time/repetitions)*1000))
Для тех, кто не знаком с Python - он открывает страницу и находит элемент - сначала с помощью локатора css, затем с помощью xpath; операция поиска повторяется 1000 раз. Результатом является общее время в секундах для 1000 повторений и среднее время для одной находки в миллисекундах.
Локаторы:
Сознательно выбран, чтобы не перестраиваться; Кроме того, селектор класса указан в CSS как «второй по скорости после идентификатора».
Среда - Chrome v66.0.3359.139, chromedriver v2.38, процессор: ULV Core M-5Y10, обычно работающий на частоте 1,5 ГГц (да, «текстовый процессор», даже не обычный зверь i7) .
Вот результат:
Очевидно, что время нахождения довольно близко; разница составляет 0,32 миллисекунды . Не прыгайте «xpath быстрее» - иногда это так, иногда css.
Давайте попробуем с другим набором локаторов, чуть более сложным - атрибутом, имеющим подстроку (общий подход, по крайней мере, для меня, идя после класса элемента, когда его часть имеет функциональное значение) :
xpath_locator = '//div[contains(@class, "button-section")]' css_locator = 'div[class~=button-section]'
Два локатора снова семантически одинаковы - «найти элемент div, имеющий в своем атрибуте класса эту подстроку».
Вот результаты:
Разница 0,15 мс .
В качестве упражнения - того же теста, что и в связанном блоге в комментариях / другом ответе - тестовая страница является общедоступной, как и тестовый код .
Они делают в коде несколько вещей - щелкая столбец для сортировки по нему, затем получают значения и проверяют правильность сортировки пользовательского интерфейса.
Я его порежу - достань только локаторы, ведь это рут-тест, да?
Тот же код, что и выше, с этими изменениями:
Теперь URL-адрес
http://the-internet.herokuapp.com/tables
; есть 2 теста.Локаторы для первого - «Поиск элементов по идентификатору и классу»:
css_locator = '#table2 tbody .dues' xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"
И вот результат:
Разница 0,2 миллисекунды.
«Поиск элементов путем обхода»:
css_locator = '#table1 tbody tr td:nth-of-type(4)' xpath_locator = "//table[@id='table1']//tr/td[4]"
Результат:
На этот раз это 0,5 мс (наоборот, xpath здесь оказался "быстрее").
Итак, 5 лет спустя (улучшенные движки браузеров) и сосредоточение внимания только на производительности локаторов (без таких действий, как сортировка в пользовательском интерфейсе и т. Д.), Та же тестовая площадка - практически нет разницы между CSS и XPath.
Итак, из xpath и css, какой из двух выбрать для повышения производительности? Ответ прост - выбирайте локацию по id .
Короче говоря, если идентификатор элемента уникален (как и должно быть в соответствии со спецификациями), его значение играет важную роль во внутреннем представлении DOM браузером и, следовательно, обычно является самым быстрым.
Тем не менее, уникальные и постоянные (например, не генерируемые автоматически) идентификаторы не всегда доступны, что подводит нас к вопросу «зачем XPath, если есть CSS?»
Преимущество XPath
Почему я думаю, что xpath лучше, если производительность не в себе? Просто - универсальность и мощность.
Xpath - это язык, разработанный для работы с XML-документами; как таковой, он позволяет создавать гораздо более мощные конструкции, чем css.
Например, навигация во всех направлениях дерева - найдите элемент, затем перейдите к его прародителю и найдите его дочерний элемент, обладающий определенными свойствами.
Он допускает встроенные логические условия -
cond1 and not(cond2 or not(cond3 and cond4))
; встроенные селекторы - «найдите div, имеющий этих дочерних элементов с этими атрибутами, и затем перемещайтесь в соответствии с ним».XPath позволяет выполнять поиск по значению узла (его тексту) - хотя эта практика не одобряется, она действительно пригодится, особенно в плохо структурированных документах (нет определенных атрибутов, на которые можно было бы наступать, таких как динамические идентификаторы и классы - найдите элемент по его тексту содержание) .
Пошаговый переход в css определенно проще - писать селекторы можно за считанные минуты; но после пары дней использования xpath быстро превосходит css по мощности и возможностям.
И чисто субъективно - сложный CSS намного труднее читать, чем сложное выражение xpath.
Outro;)
Наконец, снова очень субъективно - какой выбрать?
ИМО, нет правильного или неправильного выбора - это разные решения одной и той же проблемы, и следует выбрать то, что больше подходит для работы.
Будучи «фанатом» XPath, я не стесняюсь использовать в своих проектах и то, и другое - черт возьми, иногда гораздо быстрее просто использовать CSS, если я знаю, что он отлично справится со своей работой.
источник
[]
после//
) . Но после первого дня изучения и использования почти каждый пересекает критическую точку в кривой обучения :) (шаг в css, по общему признанию, проще, ИМХО) .Споры между cssSelector и XPath останутся одним из самых субъективных споров в сообществе Selenium . То, что мы уже знаем, можно резюмировать следующим образом:
Дэйв Хаффнер провел тест на странице с двумя таблицами данных HTML , одна таблица написана без полезных атрибутов ( ID и Class ), а другая с ними. Я подробно проанализировал процедуру тестирования и результат этого эксперимента в обсуждении. Почему я должен использовать селекторы cssSelector вместо XPath для автоматического тестирования? . Хотя этот эксперимент продемонстрировал, что каждая стратегия локатора в достаточной степени эквивалентна для разных браузеров, он не дает нам полной картины. Дэйв Хеффнер в другом обсуждении Css Vs. X Path, под микроскопомупоминалось, в тесте с конца до конца, было много других переменных при игре запуска Соус , браузер запуска и латентность и от тестируемого приложения. Неудачный вывод из этого эксперимента может заключаться в том, что один драйвер может быть быстрее другого (например, IE против Firefox ), хотя на самом деле это совсем не так. Чтобы ощутить разницу в производительности между cssSelector и XPath, нам нужно было копать глубже. Мы сделали это, запустив все с локального компьютера с помощью утилиты для тестирования производительности. Мы также сосредоточились на конкретном действии Selenium, а не на всем тестовом прогоне, и запускали все несколько раз. Я подробно проанализировал конкретную процедуру тестирования и результат этого эксперимента в обсуждении cssSelector vs XPath для селена . Но в тестах по-прежнему не хватало одного аспекта, а именно большего охвата браузером (например, Internet Explorer 9 и 10) и тестирования на более крупной и глубокой странице.
Дэйв Хеффнер в другом обсуждении Css Vs. X Path, Under a Microscope (Часть 2) упоминает, что для того, чтобы убедиться, что требуемые тесты выполняются наилучшим образом, нам нужно рассмотреть пример, демонстрирующий большую и глубокую страницу .
Испытательная установка
Чтобы продемонстрировать этот подробный пример, была установлена виртуальная машина Windows XP и установлен Ruby (1.9.3) . Также были установлены все доступные браузеры и эквивалентные им драйверы для Selenium. Для тестирования использовалась стандартная библиотека Ruby
benchmark
.Код теста
require_relative 'base' require 'benchmark' class LargeDOM < Base LOCATORS = { nested_sibling_traversal: { css: "div#siblings > div:nth-of-type(1) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3) > div:nth-of-type(3)", xpath: "//div[@id='siblings']/div[1]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]/div[3]" }, nested_sibling_traversal_by_class: { css: "div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1 > div.item-1", xpath: "//div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]/div[contains(@class, 'item-1')]" }, table_header_id_and_class: { css: "table#large-table thead .column-50", xpath: "//table[@id='large-table']//thead//*[@class='column-50']" }, table_header_id_class_and_direct_desc: { css: "table#large-table > thead .column-50", xpath: "//table[@id='large-table']/thead//*[@class='column-50']" }, table_header_traversing: { css: "table#large-table thead tr th:nth-of-type(50)", xpath: "//table[@id='large-table']//thead//tr//th[50]" }, table_header_traversing_and_direct_desc: { css: "table#large-table > thead > tr > th:nth-of-type(50)", xpath: "//table[@id='large-table']/thead/tr/th[50]" }, table_cell_id_and_class: { css: "table#large-table tbody .column-50", xpath: "//table[@id='large-table']//tbody//*[@class='column-50']" }, table_cell_id_class_and_direct_desc: { css: "table#large-table > tbody .column-50", xpath: "//table[@id='large-table']/tbody//*[@class='column-50']" }, table_cell_traversing: { css: "table#large-table tbody tr td:nth-of-type(50)", xpath: "//table[@id='large-table']//tbody//tr//td[50]" }, table_cell_traversing_and_direct_desc: { css: "table#large-table > tbody > tr > td:nth-of-type(50)", xpath: "//table[@id='large-table']/tbody/tr/td[50]" } } attr_reader :driver def initialize(driver) @driver = driver visit '/large' is_displayed?(id: 'siblings') super end # The benchmarking approach was borrowed from # http://rubylearning.com/blog/2013/06/19/how-do-i-benchmark-ruby-code/ def benchmark Benchmark.bmbm(27) do |bm| LOCATORS.each do |example, data| data.each do |strategy, locator| bm.report(example.to_s + " using " + strategy.to_s) do begin ENV['iterations'].to_i.times do |count| find(strategy => locator) end rescue Selenium::WebDriver::Error::NoSuchElementError => error puts "( 0.0 )" end end end end end end end
Полученные результаты
В виде таблицы:
В форме диаграммы:
Анализ результатов
Резюме
Мелочи
Вы можете выполнить Разметку самостоятельно, используя эту библиотеку , где Дэйв Haeffner завернутого весь код.
источник