Beginner's Guide to XPath
Follow this guide to learn the basics of XPath selectors, what they are made of and how to use them
Last updated
Follow this guide to learn the basics of XPath selectors, what they are made of and how to use them
Last updated
Knowing the basics of this versatile tool is almost a must in automated testing. Ideally, the most reliable tests are built with the use of IDs or test-IDs. In reality though there are a lot of web sites that don't utilize IDs, forcing us to use other attributes to find elements. You may also encounter cases where, as a part of your test, you need to check on certain properties of an element, like position, text content or a state of being disabled, for example. That's when XPath and CSS selectors need to be explored for writing locator expressions.
XPath, or XML Path, is a syntax expression language with its own functions and operators, that uses path expressions to select nodes or node-sets in an XML or HTML document. It is wide spread across many programming languages and testing frameworks. XPath implementations are complete in Chrome, Firefox and Edge, so XPath locators work well for cross-browser testing and can be validated right in the browser devtools.
Here's what a typical XPath selector looks like:
XPath selectors are really easy to use once you get to know how to construct them. To begin, let's take a look at the basic XPath syntax (parts in bold cannot be omitted from selector):
current node selector (/ or //)
element (tag name)
[predicate]
@attribute ="value"
function()="value"
Not every path expression will have all of these pieces in it. The look of a selector and how specific it is often depends on the context and the goal. There's always a slash or a double-slash, tag name also cannot be omitted, but it can be replaced with an asterisk symbol *, indicating that any tag name will match.
Omitting current node selector is the same as using a single slash /.
XPaths can be absolute / and relative //
absolute / XPath expressions start from the root of the HTML document and specify the complete path to the targeted element. It is recommend to avoid using absolute paths as they are vulnerable to page structure changes. Furthermore such tests are harder to read and maintain.
relative // XPath expressions start from a specified element and navigate the DOM hierarchy to locate the targeted element. Such expressions are shorter, more reliable and readable. Here's an example with the same button as before, but instead of targeting it by specifying the complete path, we have located the button by specifying its tag name and ID attribute:
Predicates are used to find a specific node or a node that contains a specific value. In other words they are extra conditions added to selector to make it more specific, hence more precise. Predicates are always enclosed in square brackets [ ] and they cannot be used by themselves - there always have to be a current node selector and a tag name. Inside the brackets, there's usually an attribute and/or a function and a value.
If you need to specify multiple predicates, there are two ways to go about it:
chaining predicates separately, enclosing each one in square brackets
//*[@id="accept"][@type="checkbox"]
using and operator inside single square brackets
//*[@id="accept" and @type="checkbox"]
Using the DOM hierarchy position of elements to make locators more specific can be very useful in certain scenarios. There are two ways you can select an element by its position in relation to its parent element (indexation starts from 1):
using its index in separate square brackets [2]
using the function [position()="2"]
Keep in mind that using the index with and operator in the same brackets will produce a faulty selector, e.g.,//*[2 and @attribute]
There are more than 200+ functions in XPath, catoring to various usage scenarios. Due to the fact that we don't need most of them in automated testing, we are only talking here about the most useful XPath functions in everyday testing routine.
Like in many high-level programming languages, calling a function in an XPath expression involves round brackets ():
Please, keep in mind that all functions are case-sensitive when matching values.
One function stands out in particular. Text() allows us to find an element by its text node aka inner text. This is a powerful tool that only XPath provides. It is particularly useful when a targeted element does not have enough attributes or unique attribute values.
Another common function is contains(), it is used for substring searches. Let's say we only want to select a certain significant part of this text value. To do that we can use the text() and the substring we are looking for as the arguments for the contains() function.
last() returns the highest index of an element among its siblings, a numeric value, hence it can be used as is. The expression in the figure below selects a button element that is the last child of a container element.
All these tools make up for a very robust and powerful selector constructor. Apart from locating elements to interact with, XPath expressions can be used as checks and often serve as test result verification steps.
Let's take a look at this simple test of a Terms and Services checkbox. In the test we are clicking on the checkbox and want to make sure that it has been checked as a result.
The expression in Step 2 finds the checkbox element by specifying its ancestor and ID attribute. Step 4 is a verification step - after the checkbox has been clicked on, it verifies that it now has the @checked attribute.
Now that we have talked about how to use XPath, we can go ahead and discuss when to use it. Please, check out the Use Cases where we are looking into the most common scenarios.
Path expression | Result |
---|---|
Path expression | Result |
---|---|
Function | Description | Example |
---|---|---|
//input[@placeholder="Enter your Email"]
selects an input element, anywhere on the page, that has a placeholder attribute with a value of "Enter your Email"
//div/button[@data-testid="save-changes"]
selects a button element that is a child of a div and has a data-testid attribute with a value of "save-changes"
//*[@for="tos-checkbox"]
selects an element, regardless of its tag name, that has a for attribute with a value of "tos-checkbox"
//img[@alt="logo"][2]
selects an img element that has an alt attribute with a value of "logo" and is the second child of an unspecified element
//tr/td[position()="3" and text()="Savings"]
selects a td element that has inner text "Savings" and is the third child of the tr element
text()
returns the text content of element
//button[text()="Execute"]
position()
returns the XPath index of element
//a[position()="2"]
last()
returns the highest index of an element among its siblings
//a[last()]
contains()
searches for a substring anywhere in the value
//*[contains(@class, "btn-primary")]
starts-with()
searches for a substring at the start of the value
//*[starts-with(text(), "Hello, Wor")]
ends-with()
searches for a substring at the end of the value
//*[ends-with(text(), "llo, World!")]