RSS Feed

Using DOM, XPath & CSS for Object Identification in QTP – Part 2

Posted on

XPath is another important Web Object Identifier introduced in QTP 11. It is one of the widely used identification strategy in open source tools. In this tutorial we will understand using XPath for locating Web objects in your application with QTP.

Introduction to XPath

XPath is used for locating nodes in an XML document and it can also be used for locating HTML elements in XHTML. XPath opens up all sorts of new possibilities for locating complex & dynamically rendered elements.

Awkwardly, developers not always follow best practices or testability guidelines while building the applications. XPath comes to your help when you don’t have a suitable id or name attribute for the element you wish to identify. You can use XPath to either identify the element in absolute terms or relative to an element that does have an id or name attribute. XPath locators can also be used to specify elements via attributes other than id and name.

Using XPath for Web Object Identification

QTP 11 has added XPath Object Identification Property to all Test Objects in Web Add-In. QTP offers two ways to use XPath, you can either instruct QTP to record Automatic XPath by settings Options in Tools menu or you can specify User Defined XPath in xpath Property.

You can also enable and use xpath Property in Object Repository or in Descriptive Programming. In following example a direct XPath query is used to identify Search Text Box on Google:

1
Browser("title:=Google").Page("title:=Google").WebEdit("xpath:=//input").Set "What is XPATH"

In above example we used //input to identify the Search Text Box. Using // in XPath query is called greedy query as it parses the entire HTML DOM until it finds the desired element. This way is useful when objects are dynamically positioned; however it takes certain amount of time to find the element.

However if you are certain about position of the desired element, you can use a direct XPath query by using single /, however please make you sure that the HTML is first node in your query. Following example shows direct XPath query for User Name Text Box on Gmail Home Page:

1
Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").WebEdit("xpath:=/html/body/div/div[2]/div/div/form/label/input").Set "myname"

Direct XPath query will find the element quicker, however if application GUI is going to change, it may fail if the element is moved into a different area of the Page.

Using Element Attributes in XPath

We can use various element attributes such as ID, Name, Value, Type etc. to identify element in following way:

1
//element [@attribute='attribute value']

In following example, ID attribute is used to identify User Name Text Box on Gmail Home Page:

1
Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").WebEdit("xpath:=//input[@id='Email']").Set "myname"

We can also use combination of attributes in XPath query so that we can try to make the element more unique for identification:

1
//element[@attribute='attribute value' and @attribute='attribute value']

In following example, we will identify and check “Stay signed in” Checkbox on Gmail Home Page:

1
Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").WebCheckBox("xpath:=//input[@id='PersistentCookie' and @type='checkbox']").Set "ON"

Identifying Elements using partial match on Attribute values

We can use XPath functions such as contains(), starts-with() & ends-with() to perform partial match on attribute values while locating the elements. This is useful when developers use Dynamic IDs or Name attributes while rendering the elements on a Page. This is commonly used in AJAX applications & frameworks.

1
2
3
//element[starts-with(@attribute,’attribute partial value')]
//element[contains(@attribute,’attribute partial value')]
//element[ends-with(@attribute,’attribute partial value')]

In following example, developers have assigned dynamic ID’s for all the input elements in following way:

1
2
3
<div id='login_area'>
    <input type='text' id='text_1'>
</div>

We can use either starts-with() or contains() function to identify this object in following way:

1
Browser("title:= Test App").Page("title:= test App").WebEdit("xpath:=//div[@id='login_area']/input[starts-with(@id, 'text_')]").Set "somevalue"
1
Browser("title:= Test App").Page("title:= test App").WebEdit("xpath:=//div[@id='login_area']/input[contains(@id, 'text_')]").Set "somevalue"

Identifying Elements using Text Contents

Locating elements by the text they contain can be quite useful. To identify elements, we need to usetext() function in XPath query. This will match the entire contents of the element.

1
//element[text()='inner text']

In following example, we will use text() function to identify the Create an account link on Gmail Home Page:

1
Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").Link("xpath:=//a[text()='Create an account']").Click

We can also use XPath functions contains(), starts-with() or ends-with() for matching partial text.

1
//element[contains(text(),'text value')]

Exmaple:

1
strLotsOfStorageMessage = Browser("title:= Gmail: Email from Google").Page("title:= Gmail: Email from Google").WebElement("xpath:=//p[contains(text(),'Over')]").GetROProperty("innerText")

Identifying Elements with XPath Axes

In simple terms XPath Axes helps to identify elements based on element’s relationship with other elements in a document. For more information, there is a nice tutorial available on W3Schools on XPath & XPath Axes.

For example in an e-commerce web application, we want to select and add items to shopping cart. However the items are listed in a table dynamically with complex HTML structure. Following figure shows the structure of the table:

shoppingcart

The textbox to enter the Quantity and image to add item was buried under layer of div elements inside a td element in a table. The ids for these elements were generated dynamically and it is difficult to add an item dynamically from the test script. Here is html code for Quantity textbox:

For Product 1

1
<input type="text" id="count_670756" value="" maxlength="3" size="3" name="qty">

For Product 2

1
<input type="text" id="count_670759" value="" maxlength="3" size="3" name="qty">

As you can see in the above code, we cannot rely on id attribute as it changes every time page is refreshed and secondly name of textbox is not unique.

We need to find a unique way to identify a Product in the table. There are two columns which contain unique values namely Product & Article column. However Article column contains primary key from the database so it is highly recommended to use this value as basis to identify the product. We can also use Product column otherwise. Following XPath Query will identify the cell containing the specified Article using XPath functions contains() & text():

1
xpath:=//td[contains(text(),'0002')]

Now we need to find the cell which contains the elements. Here we will use following-sibling axis and find the third cell from the current cell. Following query will return the cell containing Quantity & Add to cart image:

1
xpath:=//td[contains(text(),'0002')]/following-sibling::td[3]

In next step we need to get the actual elements which are located inside the layer of div elements. Here we need to use descendent axis to find the child elements in the cell:

1
2
3
xpath:=//td[contains(text(),'0002')]/following-sibling::td[3]/descendant::div[2]/input[@name='qty']
xpath:=//td[contains(text(),'0002')]/following-sibling::td[3]/descendant::div[3]/input[@name='add']

We can remove the nth element, or element Index to make this XPath query more flexible in following way:

1
2
xpath:=//td[contains(text(),'0002')]/following-sibling::td/descendant::div/input[@name='qty']
xpath:=//td[contains(text(),'0002')]/following-sibling::td/descendant::div/input[@name='add']

VBScript code to enter quantity for an Item and add selected Item to the Shopping Cart in QTP:

1
2
Browser("title:= ShopX").Page("title:= ShopX ").WebEdit("xpath:=//td[contains(text(),'0002')]/following-sibling::td/descendant::div/input[@name='qty'").Set "10"
Browser("title:= ShopX").Page("title:= ShopX ").Image("xpath:=//td[contains(text(),'0002')]/following-sibling::td/descendant::div/input[@name='add']").Click

These actions will be used to add multiple items in various test cases. We need to make these actions more generic and remove the hardcoded Article Id. Following example shows user defined function in QTP for above steps:

1
2
3
4
5
6
Public Function fnAddItemToShoppingCart(ByVal strArticleID, ByVal strQTY)
Browser("title:= ShopX").Page("title:= ShopX ").WebEdit("xpath:=//td[contains(text(),'" & strArticleID &  "')]/following-sibling::td/descendant::div/input[@name='qty'").Set strQTY
Browser("title:= ShopX").Page("title:= ShopX ").Image("xpath:=//td[contains(text(),'" & strArticleID & "')]/following-sibling::td/descendant::div/input[@name='add']").Click
End Function

However while using XPath we need to consider the fact that these locators are dependent on structure of the page and the layout of the elements. Any changes to the structure or layout will affect the locators and tests will result into failures.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: