Tech Insights

How to Choose Selectors for Automation to Make Your Life a Whole Lot Easier

Exadel QA Team

September 2, 2021

If you are excited about test automation through web interfaces (e.g. using Selenium WebDriver), you probably spend a lot of working time searching for elements like buttons, input fields, and blocks. Finding the right elements can be tricky, especially when they don’t have unique identifiers or class names. This article will help you write XPath and CSS selectors in order to find web elements efficiently.

What are Selectors and Why are They Important?

Before we proceed, there are two things that we need to clarify:

  • What is a selector? If you haven’t worked in UI automation before, you may not be familiar with them, so we’ll give you a quick overview. A selector (locator) is an object that finds and returns web items on a page based on a given query.
  • Why is it necessary to choose the right selectors? Selectors can affect the outcome, execution speed, and reliability of your tests. If your selector is too complex or contains rapidly changing attributes in its structure, there is a high probability of failure.

Let’s take a look at four attributes of a good locator:

  • Accuracy. The locator should find the element you need.
  • Uniqueness. The locator shouldn’t find anything other than the target element or group of elements.
  • Simplicity and clarity. It should be clear which element the locator refers to without examining it in the code.
  • Independence. Your locator should be universal so that if there are changes to the UI, it remains relevant.

Types of Locators in Selenium

Knowing the types of locators is only the first step to being able to use them appropriately. It’s particularly important to know how to use them when you’re working with Selenium.

There are various selectors that you can use in your scripts. Locators that can be used in Selenium are divided into following types:

  • Name and ID-based locators. These locators allow you to select an element by its ID, name, or tag name.
  • Attribute-based locators, like link text, partial link text, and class name. Link text and partial link text locators are used when you at least know part of the text of a link within an anchor tag.
  • Complex locators. This group includes XPath and CSS selectors. We will talk about them in more detail further on.

The name, ID, and attribute-based selectors are rather obvious. They are also very simple and the least useful form of selector. Complex XPath and CSS selectors are far more flexible and useful.

CSS and XPath in Selenium

XPath Selectors and their Characteristics

What is XPath and what is the use of XPath in Selenium? XPath (XML path) is a query language for addressing to nodes in an XML document. Since many browsers support XHTML, we can use XPath to locate elements in web pages.

An important difference between CSS and XPath locators in Selenium is that CSS looks for elements going down the DOM, while XPath allows you to navigate both up and down. This means that using XPath, you can find child web elements and then easily capture their parent or other ancestor. However, depending on the XPath expression, it can be rather difficult to read.

XPath’s syntax is pretty straightforward:

//tagname[@attribute=’value‘]

Where:

  • tagname

    is the type of HTML element you are looking for (e.g. div, a, p)

  • attribute

    is a property of the desired HTML element by which our locator performs the search (e.g. class)

  • value

    is the specific value you want to match

Here is a simple example to help you understand how selectors are built. Imagine you want to select a button inside a form. This button has an ID ‘submit’ and is of the type ‘button.’ So the XPath will be:

submitButton = //button[@id=’submit’]

There are a variety of XPath expressions, and they can be much more complex and sophisticated than this one. You can string together multiple elements, use logical operators, and even navigate up the document structure using ‘parent.’ To sum up, you can always create an XPath that will uniquely identify any element in your UI, but it may be rather complex and long.

If you want to navigate forward and backward through elements in DOM, XPath Axes are very useful. An axis represents a relationship to the context (current) node and is used to locate nodes relative to that node on the tree. Some useful XPath axes and their results are provided in the table below:

XPath Axis name Result
ancestor Selects all ancestors (parent, grandparent, etc.) of the current node
attribute Selects all attributes of the current node
child Selects all children of the current node
descendant-or-self Selects all descendants (children, grandchildren, etc.) of the current node and the current node itself
namespace Selects all namespace nodes of the current node
self Selects the current node

You can find more examples at the following link.

Advantages of Using XPath:

  • XPath makes it possible to navigate up the DOM
  • Locator selection is more flexible in XPath than in CSS Selector
  • If you are not sure what the element’s name is, you can use ‘contains’ to find all possible matches

There are two other useful functions we should mention: normalize-space and translate. The ‘normalize-space’ function in XPath ignores all extra white spaces (repeating, leading, trailing) in the target string, which means that text transforms to normal proper sentence version without any line breaks. The ‘translate’ function evaluates a string and a character set to be translated and returns the translated string. For example, translate (hello, hl, sr) will return a string “serro.”

CSS Selectors and Their Characteristics

What are the CSS selectors? CSS selectors in Selenium show the path to an element with a particular class, ID, or other attribute containing the information we need. Their readability is better than XPath in some situations. There are three types of CSS selectors:

  • Simple

    selectors that include only one element in their structure (e.g. p.table)

  • Combinators

    selectors that include symbols or spaces to separate elements. They can be used to select nested elements (e.g. nav ul, p > a)

  • Pseudo-selectors

    selectors that include keywords that you add to select a specific part or state of the element (e.g. p::after, p::first-letter)

HTML elements like h1p, and a belong to the simple selectors group. Combinators contain several simple CSS locators and define relationships among them. The child operator ‘div > a’ or the adjacent sibling operator ‘h3 + img’ are examples of this. As its name suggests, pseudo-selectors include pseudo-classes and pseudo-elements, such as :hover, p::first-letter.

CSS selectors follow a simple syntax:

tagname[attribute=value]

Whereas with XPath:

  • tagname

    is the type of HTML element you are looking for

  • attribute

    is a property of the desired HTML element by which our locator performs the search (e.g. class)

  • value

    is the specific value you want to match

For instance, if you need to find an image with alt text ‘learning selectors,’ the appropriate selector will be img[alt=’learning selectors’]. If you need to find an element by its class or ID, it becomes even easier. Suppose we need to find a div element with the class ‘endless’ and another element with a unique ID ‘vacation.’ The selectors will be div.endless and #vacation, respectively.

CSS selectors are great for finding elements within the current DOM. They are also the best choice for selecting elements with changing states, such as tooltips that only appear when the mouse is hovering over an element.

Advantages of Using CSS Selectors:

  • Faster than XPath
  • Easier to learn and use than XPath
  • More likely to find desired item

Once you have written an XPath expression or CSS selector, you will probably want to test it. You can do this with built-in DevTools from your browser. In order to do this, you should execute tokens $x(“some_xpath”) or $$(“css-selector”) in the Console panel, which will both evaluate and validate the selectors.

The Difference Between XPath and CSS selectors

Different people take different approaches when deciding between XPath and CSS selectors, but it’s more about personal preference than it is about the pros and cons of the options themselves.

The primary difference between XPath and CSS selectors is that with the XPath we can traverse both forward and backward, whereas a CSS selector only moves forward. Still, it’s well documented in the Selenium community that CSS selectors have the following other advantages over CSS selectors:

  • XPath engines are different in each browser, making them inconsistent
  • XPath tends to become complex, which makes it hard to read

Here are a couple of examples to compare the syntax of XPath and CSS locators:

Condition CSS selector XPath
All elements * //*
All <p> elements p //p
All child elements p>* //p/*
Select by id #start //*[@id=’start’]
Select by class .start //*[contains(@class,’start’)]
Select by attribute *[title] //*[@title]
First child of all <p> p>*:first-child //p/*[0]
All <p> elements with a child <a> not possible to find //p[a]
Next element p + * //p/following-sibling::*[0]
Previous element not possible to find //p/preceding-sibling::*[0]

To sum up, CSS selectors are more convenient to use when dealing with classes, IDs, and tag names. My advice is to use CSS selectors for simple queries based on the attributes of the element. It is especially beneficial when we look for information that is absent in DOM (using pseudo-selectors), like “a:visited”, “input:focus”, etc. CSS selectors tend to perform better, faster, and more reliably than XPath in most browsers. They are much shorter and easier to read and understand. However, there are some situations where you need to use XPath instead of CSS, like when searching for a parent element or searching for an element by its text

Best Practices for Choosing and Using Selectors

Choosing the best locator can be a lot of trouble, especially when there are so many factors. Words like “combinators” and “specificity” are listed as the most important things to consider, but several high-profile sources say that IDs (the most specific option) should be avoided at all costs! What is a poor developer or QA to do as they navigate the cascading landscape of style sheets? These ten basic rules for good locators should help clear that up:

  • In your locators, refer to elements and attributes that are not usually changed. Before writing a selector, you need to analyze the application and determine what changes frequently, and if something does change, don’t use it in the locator.
  • You’ll need to use semantic features rather than syntactic ones. When you look at an application, you can certainly name what’s there, but that’s not the most important thing. Any application solves some specific user task, and that task has its own meaning. For example, if we go to an online store, we are not interested in the tables, links, or blocks that a page contains; we care about products, price, names, etc. These are the semantic features.

Semantic attributes in the DOM are expressed as an ID, class, or other attribute. For example, a.price is a good locator because price is a semantic feature. On the other hand, a.hover-light is a poor one because hover-light is probably not a semantic feature.

  • Check uniqueness, especially if you are looking for more than one item, so that you don’t find any extra. If you find extra elements, this means that the attributes that you have already identified are not enough to remove the superfluous elements, and you have to refine the locator.
  • Anchors are necessary. This means it is better to start from a unique, stable element if you want to find child elements. Let’s look at an example. We need to find a link inside a block that corresponds to the product description. The link itself does not have any specific features, so it’s hard to find, but we know it’s inside a block that has a class product. This product block and its class represent an anchor. Therefore, in the selector we specify that the link is inside the block with class product.product a (CSS), //*[contains(@class,’start’)]//a (XPath).

And one more piece of advice: when we have several locators with the same anchor, it makes sense to declare a separate variable for the anchor. That way, if the anchor changes, you will only have to update one variable value instead of all related descendant locators.

  • Use combinations. You can use different properties of one element to select only the desired one. For example, if you have several different elements with the same class, you can use the tag name in your selector: div.product a (CSS), //div[contains(@class,’start’)]//a (XPath).

Or you can use several fragments of the same attribute: li[id=^select2-country_code][id$=US]. In this example we state the beginning and ending of an element’s ID.

Another example a[href*=user_edit]:not([href$=’user_id=1’]) finds an element that does not meet a specific property, in this case :not is used.

  • Avoid excessiveness. Making combinations is good, but sometimes you can impose too many conditions in pursuit of uniqueness. In this case, of course, the locator will be very accurate, so it will not find unnecessary elements, but it is too complicated.
  • ‘Somewhere inside’ or ‘directly inside the.’ We need to distinguish between two situations: the element is directly inside the previous element, or it is not important for us to know exactly where it is. In most cases, we use the ‘somewhere inside’ rule, but if there are many similar elements inside a parent element and you are using the ‘somewhere inside’ rule, extra elements can be found. In this case, we refine our locator with ‘>’ symbols in CSS, or ‘/’ instead of ‘//’ in XPath, which means that element is located directly inside the parent element.
  • Don’t search by text. Search by text should only be a last resort. The text often changes and modern applications tend to be international, so a text selector is very likely to crash. This applies not only to text that can be seen on the screen, but also to attributes that correspond to the tooltips and hidden types of text.
  • Do not use numbers. For example, do not refer to the first/second/third link in the block, because the order of elements can change. However, ‘first’ and ‘last’ may be useful functions when you have to work with an item from the very top or the very bottom of a list, like news feeds sorted by publish time.
  • Communicate with developers. If the selector turns out to be difficult to write, there is nothing to bind to, or it is impossible to build combinations, you need to ask the developer to add semantic attributes.

Useful Resources

To finish off, I’d like to provide you with some helpful resources for dealing with selectors. If you have a web application, CSS and XPath checker in Chrome extensions will be very useful. If you paste your selector there, the element will be highlighted on the page, along with a number of matching elements.

Below, there is an image describing how to correctly write all the different types of XPath and CSS selectors. It will help you build selectors from scratch and solve problems writing complex selectors.

This is a cool resource that helps clarify what is written in a CSS locator itself. All you have to do is paste a selector here, and you’ll get an explanation!

Last but not least, CRISP is a Google Chrome extension for automated testing that generates test code and automates time-consuming operations in test development.

Author: Uliana Pimenova