A common and
foremost concern in test automation is to locate or find elements in a page. Using xpath
reference is one of the ways to accomplish that. However, many of us are not using xpath properly as we are
not aware of its cool features. In fact, most of the times, we approach to xpath
only if there is no id or relatively unique class or name are assigned to an
element. So xpath is always our last choice. If this is how you feel then you
are not using xpath at its full potential. Sometimes I see code like the
following:
WebElement
wb=driver.findElement(By.xpath(“html/body/div[1]/div[1]/div[2]/form/div/div[2]/div/input”));
This is a not a
proper approach to use xpath. Once you do so, your code becomes vulnerable because a
simple change in the page may invalidate your element reference.
Here are some
cool ways to use xpath. Just google it and you will find lots of tutorials on xpath.
But too many examples sometimes can be perplexing. Therefore, you can use the
examples I'm going to show here as a starting point to learn xpath. Once you know how to
use it you can look for your specific requirement.
1.
Select
next element: Let’s start with the basic. Consider the following example:
Suppose you want to locate <body> element here. You can
use the following xpath expression:
“/html/body”
Similarly if you want to locate 1st
highlighted <div> here, you can use “/html/body/div”.
And for 2nd highlighted <div>, you can use “/html/body/div/div/div[2]”.
We need to notice 3 things here
- Single slash (/) at the very beginning indicates
root element
- All subsequent slashes select immediately next
child element(s)
- If there are 2 or more child elements, you can
specify any of them using index starting from 1.
2. Relative
reference: Now suppose, you have already located the 2nd
highlighted <div> in the previous example. So possibly, you have done something
like this:
"WebElement wb1=driver.findElement(By.xpath(“/html/body/div/div/div[2]”));"
Now suppose, you want to select the <form> element located
under then 2nd <div>. You can simply do the following
WebElement wb2=wb1.findElement(By.xpath(“./form”));
Please note here that as we are trying to find an
element relative to another element (wb1), we have user dot (.) in the xpath
expression. Dot (.) here indicates the current position of the element.
3. Select any element: We started looking for an
element from the root element in previous two examples. This is not a good
practice. Practically, you will want to jump to an element of your concern. For
example, in the above example, you could have refer to the body element with
the following xpath expression:
//body
Please note, we have used double slash (//) here. This looks for any appearance of the element of concern. Suppose, there are 5
input elements in a page. And they are
located in different branches, not necessarily under the same element. You can
use the following expression to get all the input elements ignoring their
parents or position in the page:
//input
And as you know by now, if you need to select a
particular input element, you can use index. For example, //input[2] locates 2nd
input element.
So in the example 1, there is only a single form
element. Therefore, you don’t have to bother for its position, rather simply
use the following expression to locate it:
//form
4. Select by attribute: Consider the following
example:
Suppose you want to locate the highlighted input
element. You cannot simply use “//input” as it will return the other input
element as well. You can use “//tbody/tr[1]/td[1]/input” but it’s untidy and not recommended. So a better approach is to use
attribute to locate element.
If you notice, the names of first and second input elements
are different. So let’s pick the "name" attribute here. The expression will look
like the following:
//input[@name=’testName’]
Here ‘@’ is used to indicate the attribute of the
element <input>. Similarly the expression “//td[@class=’class1’]” locates
<td> element with class equals ‘class1’.
User of
‘And’:
You have to be extra cautions when using attribute to
locate element. Make sure the attribute uniquely identifies the element and is
not associated with any other unwanted elements. You can use ‘and’ expression
if required. For example, you could have used “//input[@name=’testName’ and
@class=list]”. This does not add any extra benefit for this example, but you
will find this extremely helpful in real life.
5. Use of ‘contains’: We discussed about how to match attribute
in above example. Sometimes you cannot match the entire attribute. Consider the
following example:
You want to locate the highlighted <input>
element. But its id is auto generated where ‘1231-454-450’ is a dynamic data.
So you cannot simply match its id. Here you can use ‘contains’ as shown below
//input[contains(@id,'type')]
It identifies the <input> element having an id
that contains the text ‘type’. You can further use ‘and’ to specify it firmly,
“//input[contains(@id,'type') and @type='checkbox']”.
6. Select by text: Sometimes attributes are not
good enough to locate an element. You may need to locate it using the text.
Consider the following example:
Suppose you want to locate the highlighted <a>
element. You can match the text as shown below:
//a[text()='Services We Offer']
Alternately you can write
//a[.='Services We Offer']
Here dot(.) represents the text of the element.
7. Normalize-space: Consider the previous example. You
will often notice that the match does not work properly as the text contains
leading or trailing space. So it’s always a better practice to trim space
before the match. You can use ‘normalize-space’ in this regard as shown below
//a[normalize-space(text())='Services We Offer']
8. Any element: Till now we have always specified
the element tag. You can use anonymous tag. For example, if you do not want to
specify the tagname <a> in previous example then you can write “//*[normalize-space(text())='Services
We Offer']”.
Here ‘*’ indicates any element.
9. Select parent: It’s pretty easy to refer to a parent
of current element. Consider the example in item 6. Suppose you want to select
the <li> element that contains the highlighted <a> element. You can
simply do the following:
//a[text()='Services We Offer']/..
Here, double dot (..) refers to the parent element of
the current element.
10. Refer to parent by attribute: one drawback of
accessing parent element using double dot is you cannot specify attribute of
the parent. Consider the following example:
Here you have a group of checkboxes that share common
name. So you can easily locate the checkboxes. Now the requirement is to select
a checkbox provided by the text of its parent label.
i. So first of all you need to get the input
elements by name:
//input[@name='services']
This will return all checkboxes sharing common name.
ii. Then locate their parent element:
//input[@name='services']/parent::label
This will return all <label> element that is
parent of the checkboxes.
Here “parent::label” refers to the parent of the
current element. And the parent is a label.
iii. Then match the text:
//input[@name='services']/parent::label[text()='Handicraft']
This will return the label element having the
specified text.
iv. And finally select the checkbox:
//input[@name='services']/parent::label[text()='Handicraft']/input
This will return the specific checkbox.
11. Following-siblings: This is similar to ‘parent’
above. Sometimes you may need to refer to elements that are not parent or child
of another element rather siblings. Consider the following example:
Here you have some <input> element and labels
associated with them. But the relationship between the label and input element
is not a parent/child relationship. The requirement here is to identify an <input>
element by the text of the label associated with it. You can do the following:
//label[text()='First
Name']/following-sibling::div/input
Here “following-sibling::div” indicates the next <div>
elements that are siblings of the current element.
The reverse of “following-sibling” is “preceding-sibling”.
So these are the xpath techniques
I commonly use. Hopefully you will find this helpful. A final suggestion is to
install FirePath plugin for firefox. This will help you verify your xpath
before you use them in automation script.
Cheers!