33

When automating UI tests with Selenium, it is often not absolutely clear what method and what locator to use to locate an element. Some locators are less reliable, less readable than others. And, usually, there are a lot of options to get to a desired element.

To be specific, here are some things we've enforced (with ESLint) in our UI test automation project (using Protractor for testing an AngularJS application):

  • no bootstrap classes used inside CSS selectors (the idea is to use as less UI/layout-specific things to locate elements as possible)
  • no internal angular classes inside CSS selectors - things like ng-scope or ng-binding are pure-technical and should not be used inside locators
  • do not use "xpath" unless absolutely necessary (encouraged by this style guide)

The Question:

What other recommendations are there for locating elements in Selenium?

2

7 Answers 7

28
+100

Good question, especially if people will read it and stop using XPath (I am not holding my breath).

I read somewhere (cannot find the link) that another cause of flaky tests is locating elements by changeable attributes. Problem is, both browser and Selenium use internal cache to speed up access to attributes, so busting that cache might not be in sync and lead to flakiness.

  • CSS vs XPath: benchmark says that with current browsers (page has no date, sadly) difference is minimal. But because pages are styled by CSS, we can expect CSS to be more stable than XPath. Has few links for follow-up considerations.

Exception where XPath is OK: parent element of the current one (if a parent has no ID or name).

My order of preference for locating elements is: By.ID > By.NAME > By.CSS_SELECTOR > By.LINK_TEXT > By.CLASS_NAME > By.TAG_NAME > By.XPATH

9
  • 1
    I agree with pretty much all of this but I skip name in favor of CSS selector. CSS selectors are crazy fast in the browsers I've tested... to the point where they are faster than ID so that I've considered no longer using IDs. But, I think having a CSS selector of nothing but an ID is... weird. So my list is id > css >>>> xpath. For your exception list, you didn't mention finding an element by contained text... that's something only XPath can do. XPath should be used sparingly (for the reasons stated in your answer) and only when finding an element by the text contained or doing DOM traversal.
    – JeffC
    Commented Jan 2, 2018 at 5:04
  • @JeffC - You are wrong assuming that only XPath can find element by link text. By.LINK_TEXT does it in Python. In fact, my order of preference is: By.ID > By.NAME > By.CSS_SELECTOR > By.LINK_TEXT > By.CLASS_NAME > By.TAG_NAME > By.XPATH Commented Jan 2, 2018 at 15:09
  • I never said only XPath can find element by link text.. What I said was that XPath was the only locator that can find element by contained text. By.LINK_TEXT only finds an element inside of A tags. That's pretty restrictive. XPath can find elements in A tags and every other tag out there.
    – JeffC
    Commented Jan 2, 2018 at 16:18
  • @JeffC - I used the approach described to find a list of elements by other means (except XPath), then find the one by checking other attributes, including text. I believe that even if XPath is almost programming language, learning all the intricacies of it is a waste of time, because I already mastered pretty good programming language (in which rest of the test is written). Of course you are free to do whatever you want in your own free time, which includes mastering XPath. I claim that there is almost always better simpler way to do whatever XPath does. Commented Jan 2, 2018 at 16:25
  • 2
    Surely you understand that 95+% of testers can't just add IDs to whatever element they want so that's not a realistic option? Also, most people aren't going to find another job because they might need to learn a little XPath. It's not that bad and you don't have to be an expert to use it when it's necessary.
    – JeffC
    Commented Jan 2, 2018 at 22:29
20

Choosing a good locator is very important to do carefully - it will define how reliable, readable, maintainable and durable your tests are going to be; how much dependent on the UI and design changes they are gonna be. Remember: maintaining end-to-end tests is, generally speaking, difficult and expensive (good read on the subject).

Here is a set of things to think about when choosing a locator:

  • Scope. prefer "data-oriented" elements and attributes to "layout-oriented". In other words, try to not depend on page design choices. You don't, generally, want a seamless design change of a, say, container size to break your locator:

    • worse: .col-md-1.col-xs-6 input
    • better: .content input.email-input
  • say "No" to XPaths. XPath is the slowest location technique; XPath expression are generally more difficult to maintain and debug (reference). And, when it comes to multi-valued attributes like class, you need to do extra concatenation to be able to reliably match a class out of multiple class values, which would increase complexity and reduce readability:

    • worse: //*[contains(@class, 'some-class')//input[@type='text']
    • better: .some-class input.email-input
  • Technology. Try not to depend on the underlying technology of the UI implementation. For instance, in case of AngularJS, try not to use internal angular attributes or classes, like ng-scope or ng-binding

    • worse: .ng-scope.ng-binding
    • better: .email-input
  • HTML structure. Try to depend on the HTML structure of the page as less as possible. The more elements you have in your path to the desired element, the higher is a chance that a UI change would suddenly break your locator:

    • worse: .content > table > tbody > tr:nth-child(2) > td.cell > input#email
    • better: .content input#email
  • IDs are safe and fast. Searching elements by id comes down to browsers using document.getElementById() method which is optimized for speed. And, even though, nothing restricts duplicate id values on a page, they usually assumed and designed to be unique

    • worse: by.css(".content > .main-container > form.login input[type=password]")
    • better: by.id("password")

Some other related topics:

2
  • 2
    While XPaths have been shown to be slower, the gap is closing. I worry less about how fast they are because once you load a single page in a browser, you'll never see the difference in CSS selector v XPath location (probably sub 100ms at worst in common scenarios). I prefer CSS selectors because they are so much simpler, shorter, easier to read, and more consistently supported amongst browsers.
    – JeffC
    Commented Jan 2, 2018 at 5:09
  • Your HTML structure example contains an ID but you aren't using it. While some sites/pages don't conform to HTML standards (ID must be unique on the page), they should. It worries me that someone will see this and not check to see if the ID is unique on the page and use it instead of the CSS selector you have. I've seen (too many times to count) people using XPaths with nothing but IDs...
    – JeffC
    Commented Jan 2, 2018 at 5:11
9

I think most answers are pretty good, but I would like to focus a bit on the higher level of these questions and not the details.

What makes a good Selenium locator?

  • Readability: Shorter is better, preferable with a clear unique name/id which describes this unique element on the page. Feel free to change the code like classes/names/id's to make the locator and the test-code very understandable and readable.
  • Maintainability: The structure of the locator should be so uniquely good that it does not need to be updated if the location of the element in the page changes. Unless the functionality drastically changes you should be able to minimize the updating of locators and or tests.

In essence a locator should be written and read like the tester and the coder really cared about it. Clean Code matters also for page structure and tests.

What other recommendations are there for locating elements in Selenium?

  • Centralize the locators in your test-code, keep it DRY. PageObjects may help.
  • Work together with developers to create good locators! Change your process so this is possible. Good tip is to pair-program on the tests with developers if you are a separate QA person, you both could learn something.
1
  • 2
    I would say "PageObjects WILL help." Unless you are doing it wrong, I don't see how page objects wouldn't help.
    – JeffC
    Commented Jan 2, 2018 at 5:14
8

For my money it is CSS Locators. Uses ID and/or class if there is one and uses position otherwise. Plus it is super easy to get Chrome to give you a CSS selector and test it in the console tab of DevTools via document.querySelector("yourCssSelectorHere") or doing a search on the Elements tab and pasting it in.

Most experienced Selenium users recommend CSS as their locating strategy of choice as it's considerably faster than XPath and can find the most complicated objects in an intrinsic HTML document. - SeleniumHQ.org

1
  • 1
    Yes to css and ID and using chrome. Note that I don't agree with the considerable faster assertion. See elementalselenium.com/tips/32-xpath-vs-css for some info. I feel there is not much difference, it is premature optimization and css is usually more readable to many due to less characters and more spacing which is more important. However I note that the above quoted assertion comes from SeleniumHQ.org which is interesting. ymmv Commented Jun 28, 2017 at 12:35
6

What makes a good selenium selector?

  • uses css
  • unique
  • robust
  • short
  • descriptive

Given those quality attributes, in practice that translates into:

  • Favor css over xpath for readability
    e.g. favor "form.new_user input.age" over "//form[@class='new_user']/input[@class='age']"
  • Favor id's on the last element of a selector
    e.g. form.new_user input#last_name
  • Consider adding selector scope elements purely for increased readability and stability
    e.g. the form.new_user in form.new_user input.last_name lets future forms be added
  • Avoid using layout elements when possible
    e.g. avoid div section.top_header
  • Avoid longer selectors with multiple elements
    e.g. avoid div.details div.users span.user form.new_user input.last_name
  • Avoid non-english description words when possible
    e.g. avoid form.new_user56 input.lst_nm

Note that these are all good practice guidelines, not best practice rules.
They can be intentionally broken. A common example is when using frameworks that define values.

3
  • 1
    Did you run into frameworks where you cannot add unique locators? Is this because the coders did not care if the product was testable. I would expect major frameworks to care. Else switch :) Commented Jun 28, 2017 at 14:16
  • 1
    Yes, however ux folks approach and name things differently in my experience. acronyms, numbers, abstract characters, etc can be much more common. I see this in the source of all the sites I use such as cnn, weather, etc. Commented Jun 28, 2017 at 22:11
  • If an element has an ID (and the site follows HTML guidelines), you shouldn't need anything but By.id(). in your second bullet example.
    – JeffC
    Commented Jan 2, 2018 at 5:21
3

Explaining a bit more on how you can make your selector robust

Bad selector

If this is the selector you use

.content > table > tbody > tr:nth-child(2) > td.cell > input#email

And the input is moved out of table or even moved a cell, the selector breaks

Goodish selector

But if the selector was

.content input#email

Then it would work even if the input box was moved out of the table or adjusted within the table

Best selector

I'd usually use the most minimal selector though (best one)

input#email

This would be robust enough to handle any DOM changes unless there were

  • the element would change its ID (existential change that has to be accommodated for in the tests)
  • two input boxes for email with the same ID (non-case, IDs need to be unique)
1

Ideally, the most preferred locator to recognize a web-element in Selenium WebDriver is ID.

Reasons?

  • It is short.
  • It is fastest compared to other locators since in the background all it needs to do is pick the element matching the mentioned ID.
  • It’s safest, as even if the location of this element changes or worse, even if it’s type changes, your test-script shall still be able to locate and identify the element.
  • It’s robust as any changes in the surrounding elements usually don’t make any impact hence even if everything around the element changes yet the system shall be able to easily locate the element.

Then, why many Automation-QAs use other locators?

  • In an ideal world, every single element should have an ID and every single element on a particular page should have a unique ID, but it may or may not be the case always.

  • When Auto-generated IDs are used ID-locator shall not be able to detect. Worst case, duplicate IDs on a single page.

So what to do in this case? It totally depends upon the AUT, the patter developers followed to design the AUT, frequency of changes in UI-UX, the magnitude of changes in UI-UX and last but not the least, our individual preferences.

xpath though widely used if not used the strict way, can lead to issues if the location of your element changes, but till then it can uniquely identify the tiniest object on your web-page. Also, different browsers behave differently for xpath expressions.

CSS selectors and className locators fall in the same pit as xpath as whether a location may change of not, a web-application relies on its looks, and one of the key factors for its appearance is the UI. So, CSS selectors and className locators may happen to change more often than other locators.

tagName faces the issue of having multiple objects matching the given tag, hence it won’t be a preferred option, at least not my favourite.

So, apart from ID, what you choose as a selector totally depends on the factors I mentioned above and nowadays as the browsers are getting advanced I hope the differences of execution time these locators take to search in a complex developed web-application may be comparatively negligible, making the choice even harder.

Happy Testing!

1
  • 2
    You make the claim that by id is fastest... I haven't seen this. From my testing CSS selectors are faster locators. In an ideal world every element would NOT have an ID on it. That would be a TON of wasted work by dev to put IDs on everything when we probably won't need 90% of those elements. IDs on the select few is enough and won't be a burden on dev. You can still locate auto-generated IDs, depending on their form.
    – JeffC
    Commented Jan 2, 2018 at 5:25

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.