Our team includes a whole group of test engineers who write automated end2end tests for Selenium every day, and also create a huge number of selectors for them. On the one hand, this work seems easy, but in practice, conditions are added to it:
You need to write the code in a clear and consistent way, because you are not the only engineer;
You should always provide for the ability to quickly rewrite the selector;
For a whole variety of sites and layouts, it is necessary to provide a unified approach;
It is important to ensure that each selector is unambiguous;
Finally, each selector should be as informative as possible.
With all these requirements in mind, the job of a test engineer becomes not so easy, because tasks must be performed with a certain level of unification. And that’s why we have completely opted for XPath (XML Path Language) for writing selectors.
There is an opinion (and it is quite widespread) that XPath is something cumbersome, with a complex syntax. And some also question the speed of XPath searches. For example, in one of the popular courses learning about test automation with Selenium, you can see thoughts like this:
But our practice shows that this is not entirely true … or maybe even not at all. We opted for XPath because our team writes automated tests for customers – in fact, thousands of tests. To link them with the implementation of integrated monitoring systems, I already wrote about this in last post… With such volumes in a team environment, standardization of approaches is a necessity, including for the compilation of selectors.
XPath: pros and cons
Let’s start with the cons – that is, why XPath is disliked.
Minus number 1. Holivars about the speed of selectors on XPath and, for example, CSS, really do not fade away. We will not get involved in them and will not argue that this or that approach works faster. But at the same time, it should be noted that, taking into account the total execution time of the UI test, the difference in the speed of the selector is not significant at all.
Minus number 2. XPath selectors are considered uninformative by many. And this opinion is usually formed after working with browser plugins and standard browser XPath search tools. Indeed, a selector like // div/ div/ ul / li / a is not encouraging. And, by the way, we recommend not to use such tools.
Minus number 3. With all the power of XPath, there are questions that it cannot decide… For example, on XPath it will not work to make selectors for pseudo-classes and the contents of the Shadow DOM. But, as they say, each tool has its own scope.
With XPath pluses, everything is much simpler, because they are obvious and lie on the surface:
Plus # 1. The ability to search by the text of the element. The first thing we see in any web application is text. The text is also located on buttons, links, drop-down menus. And if the properties of such elements can change, then the text will most often remain the same. Thus, even changes to the layout will not affect XPath selectors in any way.
As a consequence, you can search for elements with mutable text through XPath. For example, if you want to select “the day before yesterday” in the calendar, you can explicitly write the date in the XPath selector. Also, thanks to this function, it becomes possible to create selectors for complex tables. For example, it will be useful if you need to select a cell in a certain row, and the row and column can be found only by text, since the row and column numbers can change.
Plus # 2. The presence of built-in functions such as contains (), starts-with (), sibling, normalize-space () and others, together with the logical operators and, not, or, allows you to create flexible and universal locators.
These are all very useful when it comes to real practice. Here are 3 examples in which the benefits of XPath are visible, so to speak, with the naked eye:
Example # 1. Blog post selector
Let’s take the first html code we come across.
<div id="posts" class="post-list">
<div id="post1" class="item">
<div class="title">Как я провел лето</div>
<div id="post2" class="item">
<div class="title second">Ходили купаться</div>
<div id="post3" class="item">
<div class="title">С друзьями</div>
Let’s write a selector for
<div class="title second">Ходили купаться</div>
If you do it through CSS, then the selector will look like this: .second
But please note that
post3, no classes
third! What guides the developers of such a layout and what their motives are – we will never know, but with a certain degree of probability we can say that soon there will be a class
second also will not be and therefore our tests will fall.
While staying committed to CSS, you can rewrite the selector to: # post2 .title
Yes, such a selector will live … but it is quite possible that it will not last long either.
We can see from the code that we are talking about
постах, author’s articles or blog. But what happens when the author adds another text? After all, it is logical that the new post should be the first, and then the numbering will shift, our selector # post2 .title will link to the post with the title How I spent my summer. And a wrong selector is even worse than a non-working selector, because we don’t know about the wrong selector right away, if at all.
In the meantime, on XPath, a selector might look like this:
//[normalize-space(.)='Ходили купаться' and contains(@class, 'title')]
It looks cumbersome. But how many pluses:
We have bound the selector to the title text itself, which no one can change except the author.
normalize-space (.) eliminates the problem of random / extra spaces.
The layout designer can add / change the list of used classes without consequences – the selector will remain working.
And the last, but very important opportunity: Imagine that your task is not to write a test and selectors, but to edit a selector in an existing test …, in progress …, urgently, or even better yesterday …
Which is easier to find and change, for example, in a thousand lines of code?
Option # 2 is obvious to us.
Example # 2. DOM tree search
The ability to search for elements in the DOM tree down or up allows you to use XPath to get to the deepest hidden elements of the page.
Let’s take real code
<div class="holder col-md-8 col-sm-7 col-xs-12 ">
<select data-placeholder="Выбрать..." class="form-control chosen master-field" name="field[new_object_assignment_key]" data-error-message="Поле заполнено некорректно" required="required" style="display: none;">
<option value="006002010">«Тропа» здоровья</option>
<div class="chosen-container chosen-container-single chosen-container-active loaded chosen-with-drop" title="" style="width: 505px;">
<a class="chosen-single chosen-default">
<span style="max-width: 390px;">Выбрать...</span>
<input class="chosen-search-input" type="text" autocomplete="off">
<ul class="chosen-results" tabindex="5000" style="overflow: hidden; outline: none;">
<li class="active-result result-selected highlighted" data-option-array-index="0">Выбрать...</li>
<li class="active-result" data-option-array-index="1">«Тропа» здоровья</li>
This is an example of a dropdown menu on a page. First we need to click on the link ( tag), then fill in the option ( tag), and then select it (
Check out how elegant and unambiguous the XPath selectors look like in this case:
Click on the menu:
Here we pushed away from the existing name field, went up the tree, and then returned to the desired tags. Replace the value of the name field and the option name with variables and selectors will be universal for any of the menus on the page.
Example No. 3. XPath does where others fail
Tables are quite common on web pages. In this case, we often talk about many tables on one page, and each of them can contain hundreds of rows.
Below we provide only a part of the (simplified) code.
How to make a selector for the cell link “On approval”? The classes remained somewhere at the top, the tags are all the same, there are no attributes from the word “at all” … XPath does a great job here, thanks to its functions and full-text search:
Such a selector is easy to read, which means it is easy to edit it if necessary.
By the way, XPath demonstrates additional flexibility here. If in the example above “On approval” there is a whole set of “Purchases”, then we can add the purchase number as another condition. For example, like this:
//*[contains(.,'Сделки')]//tr[contains(.,'Закупка 4')]//td[contains(.,'На согласовании')]//a
Bottom line: why XPath?
In fact, XPath is similar to a programming language: a good XPath selector is easy to read, it is immediately clear which element is being discussed. This state of affairs adds convenience in working with XPath, increases the speed of performing typical tasks and, as a result, reduces development time.
In addition, XPath allows you to search in general on any attribute of an element. Developers often add their own attributes to many tags. You can’t find them through CSS and standard testing framework methods. XPath also helps out here, for example, this is how you can make a selector for a custom attribute:
Summing up, I will say that we have chosen XPath for ourselves as the most convenient tool for creating selectors and are happy to share our experience both with customers and with colleagues “on the shop floor”. But not every selector written in XPath is uniquely good. In the next post, I’ll go into more detail about the “bad” and “good” XPath practices that we have identified by bumping our own. And now I ask everyone interested to participate in our survey.