Thursday, July 10, 2014

Test Automation: XPATH examples

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!