When automating any web application with Selenium you will come across page elements for which there is no unique id or name. Locating by class name or tag name can rarely be used as those will return multiple elements in a page. Locating by link text works only for links (<a> tags). In such scenarios XPath is the locating technique which comes for the rescue. You can literally locate any element in your web page using XPath no matter where on the page the element appears and whether the element contains any attribute or not.
Here are the scenarios where XPath can be used:
- Locating elements with standard attributes [id, name, class, etc]
- Locating elements with non-standard attributes which are specific to your web page
- Locating elements with partial attribute name
- Locating elements with displayed text or part of displayed text
- Locating elements relative to a known element
XPath can be absolute or relative. For absolute xpath always starts at the root html tag and you need to specify each and every tag that comes in between the html tag and the element you want to find. Lets consider below html snippet:
The absolute xpath to locate the highlighted "input" element will be:
/html/body/div[1]/div[1]/form/input[@name='q']
The xpath string will keep on getting longer for the elements which does not appear at the top of the page and any addition or removal of tags in between will break the xpath. For this reason one should never use absolute xpaths.
The relative xpath syntax is similar to absolute xpath but it starts with // and doesn't need to be started with the "html" tag. Relative xpath for the same "input" element will be:
//input[@name='q']
Locating elements with attributes:
The xpath syntax for locating elements by attributes is:
//tagName[@attributeName='attributeValue']
Examples:
//input[@name='q'] - will find the "input" element having name attribute 'q']
//*[@id='query'] - will find any element having id attribute 'query']
Multiple attributes can also be used together as below:
//input[@name='q'][@class='textfield']
Locating elements with partial attribute name
You may come across scenarios when the attribute value is not static and it changes from one context to another. Lets consider the below snippet where the title of the logout link also contain the user name:
One thing that is common in the above two cases is that the title attribute contains "logOut" in the attribute value. The xpath for the logout link which will work for both the users is as below:
//a[contains(@id,'logOut')]
"contains" here is called a xpath function and it checks the attribute 'id' contains 'logOut' in the value.
Locating elements with displayed text or partial text
You will come across elements which doesn't contain any useful attribute but there is a displayed text. Consider the below html snippet:The "div" has a class attribute but many other elements in the page might have the same class attribute. We can't use class attribute to uniquely identify the above div element. We can use text() function of xpath to locate the above div as below:
//div[text()='A testing framework for the JVM']
Suppose we don't want to specify the whole displayed text to locate the element, we want to use part of the displayed text. We can combine text() with contains() to achieve this as below:
//div[contains(text(), 'A testing framework for the JVM')]
Locating elements relative to a known element using xpath axes
In scenarios when the element you want to locate doesn't have any useful attribute or displayed text to identify, the only option left is to locate the element relative to a known element. Xpath is the only locating technique which can be used to locate related elements at any level in the html DOM. There are thirteen different axes in xpath as listed in the below table. The highlighted ones are the most frequently used and almost any element in your web page can be located using those only.
Axis name
|
When to use
|
ancestor
|
To
select all ancestors (parent, grandparent, etc.) of the current node
|
ancestor-or-self
|
To
select all ancestors (parent, grandparent, etc.) of the current node and the
current node itself
|
attribute
|
To select all
attributes of the current node
|
child
|
To select all
children of the current node
|
descendant
|
To select all
descendants (children, grandchildren, etc.) of the current node
|
descendant-or-self
|
To select all
descendants (children, grandchildren, etc.) of the current node and the
current node itself
|
following
|
To select everything
in the document after the closing tag of the current node
|
following-sibling
|
To select all
siblings appearing after the current node
|
namespace
|
To select all
namespace nodes of the current node
|
parent
|
To select the
parent of the current node
|
preceding
|
To select all
nodes that appear before the current node in the document, except ancestors,
attribute nodes and namespace nodes
|
preceding-sibling
|
To select all
nodes that appear before the current node in the document, except ancestors,
attribute nodes and namespace nodes
|
self
|
To select the
current node
|
Lets see how the highlighted xpath axes can be used to locate elements in a html grid.
Xpath axis syntax:
The syntax for using xpath axis is:
//knownXpath/axisName::elementName
Example 1:
Get the grid row ("tr") for employee name "Pradip Patra"
//td[text()='Pradip Patra']/ancestor::tr
or
//td[text()='Pradip Patra']/parent::tr
Example 2:
Get the checkbox for the grid row for employee name "Pradip Patra"
//td[text()='Pradip Patra']/parent::tr/td[1]
And this is how the xpath is built:
- Get the "td" with text "Pradip Patra"
- Go to the parent "tr"
- Get the first "td" child of the above "tr" [td[1] indicates that the position of the located "td" is at position 1]
Al alternative xpath for the above scenario using "preceding-sibling" can be:
//td[text()='Pradip Patra']/preceding-sibling::/td[1]
Example 3:
Get the position [CEO] of "Advik Patra"
//td[text()='Advik Patra']/following-sibling::td