XPath is a tool used to locate elements on a webpage. Testers use it to interact with buttons, text fields, or links during automated testing. Mastering XPath 101 will help you create more stable test scripts and find elements on complex web pages where other locator strategies might not work.
If you’re new to XPath or looking to sharpen those skills, this post will walk you through everything you need to know.
XPath Locator Strategies for Selenium Testing
XPath locator strategies are different approaches that you can use to interact with elements in a web page. We’ll be discussing some of those strategies in this blog today.
-
Understanding XPath’s Basics
XPath is a way to find elements using its HTML structure. There are two types of XPath–Absolute and relative. Absolute XPath is a direct path from the root of the HTML to the target element. It’s to the point but fragile, i.e., small changes in the layout can break it. Relative path, on the other han,d starts from any point in the HTML, but not the root. It’s shorter, more flexible, and preferred for testing.
XPath works by targeting elements through their attributes, text, or position. For example:
- To find a button with the text “Submit”:
//button[text()=’Submit’]
- To find an element using its attribute (like an ID or class):
//button[@id=’submit-btn’]
-
Using XPath Functions
XPath functions will refine your element selection process. They create locators that adapt to changes in the web page so that your test becomes more durable.
A few such functions are:
- contains(): This function checks if an attribute has a specific value or not. Because if it does, it will be useful for partial matches.
Example:
//div[contains(@class, ‘menu’)]
This locates any <div> element whose class contains “menu”.
- starts-with(): It is used to find elements whose attributes start with a certain value.
Example:
//input[starts-with(@id, ‘user’)]
This targets any <input> element with an ID starting with “user”.
- text(): This function selects elements based on their visible text
Example:
//button[text()=’Cancel’]
This finds a button with the exact text “Cancel”.
- normalize-space(): This removes extra space from the text
Example:
//h1[normalize-space(text())=’Welcome’]
This locates an <h1> element with the text “Welcome”, ignoring any extra spaces.
3) Handling Dynamic Elements
Webpages aren’t always completely static. They sometimes have dynamic content where attributes like ID or class name frequently change. This breaks your locators if you rely on fixed values. You must use techniques that make your XPath expressions more adaptable if you want to handle dynamic elements like a pro.
Those techniques are:
- Use contains() for partial matches: First and foremost, do not target the dynamic content. Focus on the stable part of it. Example:
//input[contains(@id, ‘username’)]
This XPath will match any <input> element with an ID that contains “username,” even if the rest of the ID changes dynamically.
- Combine multiple attributes: Combine different attributes of an element to make your locator more precise. Example:
//button[@class=’btn-primary’ and @type=’submit’]
This targets a button that matches both the class and type attributes.
- Use starts-with() for dynamic IDs: If IDs change but follow a predictable pattern, you can use starts-with() to catch that pattern. Example:
//div[starts-with(@id, ‘user-‘)]
4) Use XPath Axes
XPath axes find elements based on their position or relationship with other elements in the document. This makes it easier to handle complex page structures where standard attribute-based locators are not enough.
Some useful axes to master are: XPath 101
- parent:: – This axis selects the parent of the current node. Example:
//span[text()=’Submit’]/parent::button
- Following-sibling:: – This selects an element that is a sibling of the current node and comes after it. Example:
//label[text()=’Username’]/following-sibling::input
- Preceding-sibling:: –This selects a sibling element that comes before the current node. Example:
//div[@class=’form-group’]/preceding-sibling::h3
- Ancestor:: – This selects any ancestor of the current node. Example:
//span[text()=’Save’]/ancestor::div[@class=’container’]
- Descendant:: –This selects all children and grandchildren of the current node. Example:
//div[@class=’menu’]/descendant::a
5) Optimizing XPath Performance XPath 101
XPath expressions must be written effectively otherwise, they will slow down tests and on pages with lots of elements. But, no worries! There are a few ways through which we can optimize XPath performance. Those are:
- Avoid Absolute XPath: Absolute XPath starts from the root of the document so that it becomes difficult to break when the page structure changes. It also slows down your tests by making Selenium traverse the entire DOM. So, always prefer Relative XPath, which is faster and more flexible.
- Use Specific Attributes: Don’t search for general element types. Narrow down the search with specific attributes instead. Example:
//input[@type=’text’ and @name=’username’]
- Avoid Wildcards: Wildcard searches the entire DOM, which is quite inefficient. Be as precise as possible when defining your elements.
- Limit the Search Scope: Instead of searching the entire document, focus on a smaller section. If you know the element is inside a certain div or section, use that as a base. Example:
//div[@id=’form’]//input[@name=’email’]
- Use Indexed XPath Only When Necessary: While XPath allows you to use indexes, it’s better to avoid this when possible. Index-based XPath expressions break easily if the structure of the page changes or if new elements are added.
6) Advanced XPath Techniques XPath 101
At times, basic XPath strategies are not enough when dealing with complex or dynamically generated web pages. That’s when you have to become crafty and create custom XPath expressions to target specific elements more precisely.
4 different ways to approach this complex scenario are:
- Target elements by position: When there are no unique attributes, use XPath functions like position() or last() to locate elements based on their place in the DOM. Example:
(//div[@class=’item’])[last()]
- Use conditions in XPath: Create complex conditions using and, or, or nested conditions. Example:
//input[@type=’text’ and (@id=’user’ or @name=’username’)]
- Handling Elements with Dynamic Text: If the text of an element changes dynamically– use partial matching functions like contains() or normalize-space() to account for changes in the content. Example:
//button[contains(text(),’Sign In’)]
- Chaining Multiple XPath Expressions: Sometimes, you need to combine several locators to pinpoint an element. Let’s say an element is within a table or grid; you can chain together multiple XPath conditions to locate it. Example:
//table[@id=’user-table’]//tr[2]//td[3]
7) Handle Multiple Matches With XPath
Consider this: What if an XPath expression returns more than one match? When this happens, you need strategies to either select the correct element or handle all of them at once.
But how to do that? Let’s find out!
- Use Indexes: If you know which element you want from a group of matches, you can use an index to select it. Example:
(//input[@type=’checkbox’])[3]
- Loop Through Elements: When your XPath returns multiple elements, loop through them in Selenium to interact with each one. Example:
checkboxes = driver.find_elements_by_xpath(“//input[@type=’checkbox’]”)
for checkbox in checkboxes:
checkbox.click()
- Target Specific Elements in a Group: Sometimes, you need to select an element from a group based on other conditions, like specific attributes or text. This is when you should refine your XPath to focus on additional details. Example:
//div[@class=’product’][@data-category=’electronics’]
- Combining Conditions: Refine your XPath to handle multiple matches by combining attributes or using functions like contains() or starts-with() to target specific elements. Example:
(//a[contains(@href, ‘products’)])[1]
8) Leveraging XPath’s self and preceding Axes for Better Control
The self and preceding axes help you locate elements in situations where standard attribute-based XPath strategies fail.
- self:: Axis: This axis selects the current node itself. It’s useful when combined with conditions to validate the current element. Example:
//input[@type=’submit’]/self::input[@class=’btn-primary’]
- preceding:: Axis: This axis selects all elements that come before the current node in the document to make it helpful when elements appear earlier in the DOM but are still related to your target. Example:
//div[@class=’form-group’]/preceding::h3[1]
As you implement the XPath locator strategies, it becomes essential to ensure that your Selenium tests work seamlessly across different browsers and devices.
You can use a cloud-based platform like LambdaTest that can help you run your Selenium tests on a wide range of browsers and operating systems without the hassle of setting up and maintaining your infrastructure. LambdaTest is an AI-powered test orchestration and execution platform that lets you run manual and automated test scenarios at scale with over 3000+ real devices, browsers and OS combinations.
By integrating LambdaTest into your testing workflow, you can quickly validate the robustness of your XPath locators.
Conclusion: XPath 101
Mastering XPath 101 locator strategies will help you become a confident Selenium tester. You just need to get a grip on basic XPath functions and learn how to handle dynamic elements.
XPath gives the flexibility to locate elements even when things get complicated. Remember, it’s okay to make mistakes while you practice—each one is a chance to learn and grow. With time and experimentation, you’ll develop the skills to create test scripts that are not just effective but also reliable.
So, keep exploring and trying out different strategies. The more you practice, the more comfortable you’ll feel. And without even realizing it, you’ll be navigating complex web pages with ease.