CSS selectors perform far better than Xpath and it is well documented in Selenium community. Here are some reasons,
- Xpath engines are different in each browser, hence make them inconsistent
- IE does not have a native xpath engine, therefore selenium injects its own xpath engine for compatibility of its API. Hence we lose the advantage of using native browser features that WebDriver inherently promotes.
- Xpath tend to become complex and hence make hard to read in my opinion
However there are some situations where, you need to use xpath, for example, searching for a parent element or searching element by its text (I wouldn\'t recommend the later).
You can read blog from Simon here . He also recommends CSS over Xpath.
If you are testing content then do not use selectors that are dependent on the content of the elements. That will be a maintenance nightmare for every locale. Try talking with developers and use techniques that they used to externalize the text in the application, like dictionaries or resource bundles etc. Here is my blog that explains it in detail.
edit 1
Thanks to @parishodak, here is the link which provides the numbers proving that CSS performance is better
I’m going to hold the unpopular on SO selenium tag opinion that XPATH is preferable to CSS in the longer run.
Let me first tackle \"the elephant in the room\" – xpath is slower than css.
With the current cpu power (read: anything produced in the last 5 years), even on browserstack/saucelabs VMs, and the development of the browsers (read: all the popular ones in the last 5 years) that is hardly the case. The browser\'s engines have developed, the support of xpath is uniform, IE is out of the picture (hopefully for most of us). This comparison in the other answer is being cited all over the place, but it is very contextual – how many are running – or care about – against IE8?
If there is a difference, it is in a fraction of a millisecond.
Yet, most higher-level frameworks add at least 1ms of overhead over the raw selenium call anyways (wrappers, handlers, state storing etc); my personal weapon of choice – robotframework – adds at least 2ms, which I am more than happy to sacrifice for what it provides. A network roundtrip from an AWS us-east-1 to BrowserStack\'s hub is usually 11 milliseconds.
So with remote browsers if there is a difference between xpath and css, it is overshadowed by everything else.
There are not that many public comparisons (I\'ve really seen the cited one), so – here\'s a rough single-case, dummy and simple one. The target – BrowserStack\'s landing page, and it\'s \"Sign Up\" button; a screenshot of the html:
Here\'s the test code (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))
For those who are not familiar with python – it opens the page, and finds the element – first with the css locator, then with the xpath; the find operation is repeated 1,000 times. The output is the total time in seconds for the 1,000 repetitions, and average time for one find in milliseconds.
The locators are – for xpath – \"a div element having this exact class value, somewhere in the DOM\"; the css is similar – \"a div element with this class, somewhere in the DOM\". Deliberately chosen not to be over-tuned; also, the class selector is cited for the css as \"the second fastest after an id\".
The environment – Chrome v66.0.3359.139, chromedriver v2.38, cpu: ULV Core M-5Y10 usually running at 1.5GHz (yes, a \"word-processing\" one, not even a regular i7 beast).
Here\'s the output:
css total time 1000 repeats: 8.84s, per find: 8.84ms
xpath total time for 1000 repeats: 8.52s, per find: 8.52ms
Obviously the per find timings are pretty close; the difference is 0.32 milliseconds. Don\'t jump – the xpath is faster – sometimes it is, sometimes it\'s css.
Let\'s try with another set of locators, a tiny-bit more complicated – an attribute having a substring (common approach at least for me, going after an element\'s class when a part of it bears functional meaning):
xpath_locator = \'//div[contains(@class, \"button-section\")]\'
css_locator = \'div[class~=button-section]\'
The two locators are again semantically the same – \"find a div element having in its class attribute this substring\".
Here are the results:
css total time 1000 repeats: 8.60s, per find: 8.60ms
xpath total time for 1000 repeats: 8.75s, per find: 8.75ms
Diff of 0.15ms.
With a more complex DOM, the results are the same; I don\'t have any publicly available URLs at hand to give an example, but have seen again similar results for xpath and css.
As an exercise - the same test as done in the linked blog in the comments/other answer - the test page is public, and so is the testing code.
They are doing a couple of things in the code - clicking on a column to sort by it, then getting the values, and checking the UI sort is correct. I\'ll cut it - just get the locators, after all this is the root test, right?
The same code as above, with these changes in:
The url is now http://the-internet.herokuapp.com/tables
; there are 2 tests.
The locators for the first one - \"Finding Elements By ID and Class\" - are:
css_locator = \'#table2 tbody .dues\'
xpath_locator = \"//table[@id=\'table2\']//tr/td[contains(@class,\'dues\')]\"
The outcome:
css total time 1000 repeats: 8.24s, per find: 8.24ms
xpath total time for 1000 repeats: 8.45s, per find: 8.45ms
Diff of 0.2 milliseconds.
The \"Finding Elements By Traversing\":
css_locator = \'#table1 tbody tr td:nth-of-type(4)\'
xpath_locator = \"//table[@id=\'table1\']//tr/td[4]\"
The result:
css total time 1000 repeats: 9.29s, per find: 9.29ms
xpath total time for 1000 repeats: 8.79s, per find: 8.79ms
This time it is 0.5 ms (in reverse, xpath turned out \"faster\" here).
So, out of xpath and css, which of the two to choose for performance? The answer is simple – choose locating by id.
Long story short, if the id of an element is unique (as it\'s supposed to be according to the specs), its value plays an important role in the browser\'s internal representation of the DOM, and thus is usually the fastest.
With the performance out of the picture, why do I think xpath is better? Simple – versatility, and power.
Xpath is a language developed for working with XML documents; as such, it allows for much more powerful constructs than css.
For example, navigation in every direction in the tree – find an element, then go to its grandparent and search for a child of it having certain properties. It allows embedded boolean conditions – cond1 and not(cond2 or not(cond3 and cond4)); embedded selectors – \"find a div having these children with these attributes, and then navigate according to it\". It allows searching based on a node\'s value – however frowned upon this practice is, it does come in handy especially in badly structured documents (no definite attributes to step on, like dynamic ids and classes).
The stepping in css is definitely easier – one can start writing selectors in a matter of minutes; but after a couple of days of usage, the power and possibilities xpath has quickly overcomes css.
And purely subjective – a complex css is much harder to read than a complex xpath expression.
Finally, again very subjective - which one to chose? IMHO, there is no right or wrong choice - they are different solutions to the same problem, and whatever is more suitable for the job should be picked. Being fan of xpath I\'m not shy to use in my projects a mix of both - heck, sometimes it is much faster to just throw a css one, if I know it will do the work just fine.