私は多言語WebアプリケーションでSelenium WebDriver 2.25.0を使用しており、主にページコンテンツをテストしています(アラビア語、英語、ロシア語などの異なる言語用)。
パフォーマンスに応じて優れたアプリケーションの場合、すべてのブラウザをサポートする必要があります(つまり、IE 7,8,9、FF、Chromeなど)。
貴重なご提案を事前に感謝します。
CSSセレクターのパフォーマンスはXpathよりもはるかに優れており、Seleniumコミュニティで十分に文書化されています。いくつかの理由があります、
ただし、親要素を検索したり、テキストで要素を検索したりするなど、xpathを使用する必要がある場合があります(後者はお勧めしません)。
Simon here からブログを読むことができます。また、XpathよりもCSSを推奨しています。
コンテンツをテストする場合は、要素のコンテンツに依存するセレクターを使用しないでください。それはすべてのロケールのメンテナンスの悪夢です。開発者と話をして、辞書やリソースバンドルなど、アプリケーションでテキストを外部化するために使用した手法を使用してみてください。詳細を説明する blog .
@parishodakのおかげで、CSSのパフォーマンスが優れていることを証明する数値を提供する link があります。
長期的にはCSSに対してXPathのほうが望ましいというSO Seleniumタグの意見については、人気がありません。
この長い投稿には2つのセクションがあります-最初に、ナプキンの裏側に、2つのパフォーマンスの違いが.1-0.3ミリ秒(yes ;それは100 micro seconds)であり、XPathがより強力である理由を共有します。
最初に「部屋の象」に取り組みましょう。xpathはcssよりも遅いです。
現在のCPU能力(2013年以降に作成されたx86のすべて)、browserstack/saucelabs/aws VM、およびブラウザの開発(読み取り:過去5年間で人気のあるすべてのもの)それはほとんどありません。ブラウザのエンジンが開発されました。xpathのサポートは統一されており、IEは画像の外にあります(うまくいけばほとんどの人にとって)。他の答えのこの比較はあちこちで引用されていますが、IE8に対する自動化は非常に文脈的で、何人が実行されていますか?
差がある場合、ミリ秒の小数部にあります。
しかし、ほとんどの高レベルのフレームワークは、未加工のSelenium呼び出し(ラッパー、ハンドラー、状態の保存など)に少なくとも1msのオーバーヘッドを追加します。私が選んだ武器-RobotFramework-は少なくとも2msを追加しますが、それが提供するものを犠牲にして喜んでいます。 AWS us-east-1からBrowserStackのハブへのネットワークラウンドトリップは、通常11ミリ秒です。
そのため、リモートブラウザの場合、xpathとcssの間に違いがあると、他のすべての要素によって桁違いに隠れてしまいます。
それほど多くの公開比較はありません(私は実際に引用されたものだけを見ました)–だから–ここでは大まかな単一のケース、ダミーのシンプルなものです。
2つの戦略によってX回要素を見つけ、その平均時間を比較します。
ターゲット– BrowserStackのランディングページ、およびその「サインアップ」ボタン。この投稿を書いているhtmlのスクリーンショット:
テストコードは次のとおりです(python)。
from Selenium import webdriver
import timeit
if __== '__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で。検索操作は1,000回繰り返されます。出力は、1,000回の繰り返しの合計時間(秒単位)と、1回の検索の平均時間(ミリ秒単位)です。
ロケーターは次のとおりです。
意図的にオーバーチューニングされないように選択されています。また、クラスセレクターはcssに対して「idに続く2番目に速い」として引用されています。
環境-Chrome v66.0.3359.139、chromedriver v2.38、cpu:通常1.5GHzで実行されるULVコアM-5Y10(はい、「ワープロ「1つ、通常のi7獣でもない)。
出力は次のとおりです。
css total time 1000 repeats: 8.84s, per find: 8.84ms xpath total time for 1000 repeats: 8.52s, per find: 8.52ms
明らかに、検索ごとのタイミングはかなり近いです。違いは.32millisecondsです。 「xpathの方が速い」というジャンプは避けてください-場合によってはcssです。
もう少し複雑なロケーターを試してみましょう。サブストリング(属性の一部が機能的な意味を持つ場合、少なくとも私にとっては一般的なアプローチです。 ):
xpath_locator = '//div[contains(@class, "button-section")]'
css_locator = 'div[class~=button-section]'
2つのロケーターも意味的に同じです-「クラス属性にこのサブストリングを持つdiv要素を見つけます」。
結果は次のとおりです。
css total time 1000 repeats: 8.60s, per find: 8.60ms xpath total time for 1000 repeats: 8.75s, per find: 8.75ms
.15msの差分。
演習として-コメント/その他の回答の リンクされたブログ で行われたのと同じテスト- テストページ は公開されているため、 テストコード 。
彼らはコードでいくつかのことを行っています-列をクリックしてソートし、値を取得し、UIのソートが正しいことを確認します。
切り取ります-ロケーターを取得するだけです、結局のところ-これはルートテストですよね?
上記と同じコード。これらの変更は次のとおりです。
URLはhttp://the-internet.herokuapp.com/tables
になりました。 2つのテストがあります。
最初のロケーター-「IDおよびクラスによる要素の検索」は次のとおりです。
css_locator = '#table2 tbody .dues'
xpath_locator = "//table[@id='table2']//tr/td[contains(@class,'dues')]"
そして、結果は次のとおりです。
css total time 1000 repeats: 8.24s, per find: 8.24ms xpath total time for 1000 repeats: 8.45s, per find: 8.45ms
.2ミリ秒の差分。
「トラバースによる要素の検索」:
css_locator = '#table1 tbody tr td:nth-of-type(4)'
xpath_locator = "//table[@id='table1']//tr/td[4]"
結果:
css total time 1000 repeats: 9.29s, per find: 9.29ms xpath total time for 1000 repeats: 8.79s, per find: 8.79ms
今回は.5 msです(逆に、xpathはここで「高速」になりました)。
5年後(より良いブラウザーエンジン)andロケーターのパフォーマンスのみ(UIでの並べ替えなどのアクションはなし)、同じテストベッド-実際にはありませんCSSとXPathの違い。
では、xpathとcssのうち、どちらをパフォーマンスのために選択するのでしょうか?答えは簡単です。id by by idを選択してください。
要するに、要素のidが一意である場合(仕様に従っていると想定される場合)、その値はブラウザーのDOMの内部表現で重要な役割を果たし、したがって通常は最速です。
しかし、一意で一定の(たとえば、自動生成されない)idは常に使用できるとは限らないため、「CSSがあるのにXPathが必要な理由」につながります。
パフォーマンスが見えないのに、なぜxpathの方が優れていると思いますか?シンプル–汎用性とパワー。
Xpathは、XMLドキュメントを操作するために開発された言語です。そのため、cssよりもはるかに強力な構成が可能です。
たとえば、ツリー内のあらゆる方向へのナビゲーション–要素を見つけ、その祖父母に移動して、特定のプロパティを持つその子を検索します。
埋め込みブール条件を許可します– cond1 and not(cond2 or not(cond3 and cond4))
;埋め込みセレクタ-「これらの属性を持つこれらの子を持つdivを見つけて、それに従ってナビゲートします」。
XPathは、ノードの値(テキスト)に基づいて検索を許可します。ただし、このプラクティスに眉をひそめたとしても、特に構造の悪いドキュメントでは役立ちます(明確な属性なしon、動的IDやクラスのように、テキストコンテンツで要素を見つけます)。
Cssのステップインは間違いなく簡単です。数分でセレクターの作成を開始できます。しかし、数日間使用した後、xpathの威力と可能性はすぐにCSSを克服しました。
そして純粋に主観的–複雑なcssは、複雑なxpath式よりも読みにくいです。
最後に、再び非常に主観的-どちらを選択しますか?
私見、正しいまたは間違った選択はありません-それらは同じ問題に対する異なる解決策であり、仕事により適したものは何でも選ぶべきです。
XPathの「ファン」であるため、私のプロジェクトで両方を組み合わせて使用することを恥ずかしがり屋ではありません。
cssSelector vs XPathの間の議論は、Selenium Communityで最も主観的な議論の1つとして残っています。これまでにすでにわかっていることは、次のように要約できます。
Dave Haeffner実行 test on 2つのHTMLデータテーブルがあるページ 、 1つのテーブルは有用な属性(IDおよびClass)なしで記述されており、もう1つのテーブルは属性付きです。 テスト手順と、この実験の結果を詳細に議論しました 自動テストにXPathではなくcssSelectorセレクターを使用する理由は何ですか? 。この実験は、各 Locator Strategy がブラウザー間で合理的に同等であることを実証しましたが、全体像を適切にペイントできませんでした。 Dave Haeffner他の議論で Css Vs. X Path、Under a Microscope 言及されましたが、エンドツーエンドのテストでは他にも多くの変数が使われていました- ソースの起動、ブラウザの起動、およびレイテンシテスト中のアプリケーションとの間でやり取りします。その実験からの不幸な点は、実際にはそうではなかったのに、一方のドライバーが他方のドライバーよりも高速である可能性があることです(たとえばすなわち vs Firefox)まったく。 cssSelectorとXPathのパフォーマンスの違いを理解するには、さらに掘り下げる必要がありました。パフォーマンスベンチマークユーティリティを使用しながら、ローカルマシンからすべてを実行することでそれを行いました。また、テスト実行全体ではなく、特定のSeleniumアクションに注目し、何度も実行しました。特定のテスト手順と、この実験の結果を詳細に議論しました cssSelector vs Xele for Selenium 。しかし、テストにはまだ1つの側面が欠けていました。つまり、より多くのブラウザのカバレッジ(たとえば、Internet Explorer 9および10)およびより大きくより深いページに対するテスト
Dave Haeffner別の議論で Css Vs. X Path、Under a Microscope(Part 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
NOTE:出力は秒単位で、結果は100回の実行の合計実行時間です。
テーブル形式:
グラフ形式:
this library where Dave Haeffnerを使用して、すべてのコードをラップして、自分でベンチマーキングを実行できます。