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

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:


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:

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:


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

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


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:


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

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


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.


//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:

<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:


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

 

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 use text() function in XPath query. This will match the entire contents of the element.


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

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


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.


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

Exmaple:


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

Shopping Cart Page

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


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

For Product 2


<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():


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:


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:


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:


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:


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:


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.


23 Comments on “Using DOM, XPath & CSS for Object Identification in QTP – Part 2”

  1. […] Using DOM, XPath & CSS for Object Identification in QTP – Part 2 […]

  2. N00bQAtester says:

    This has really helped. Using Xpath to identify some dynamic identifiers has worked wonders. Currently I’m trying to take one of those elements, get the property and input into the table using the code below

    datatable.Value(“Prices”) = Browser(“*Browser*”).Page(“*Page*”).WebElement(“xpath:/html
    /body/form/div[5]/div/div/div/div[2]/div/table/tbody/tr[2]/td[3]/span”).GetROProperty(“innertext”)

    I get the following message
    WebElement object was not found in the Object Repository.
    Check the Object Repository to confirm that the object exists or to find the correct name for the object.

    Since we are finding it with xpath then it will not be in the repository. Any ideas

    • Remz says:

      Try to use this instead:

      datatable.Value(“Prices”) = Browser(“*Browser*”).Page(“*Page*”).WebElement(“xpath:=/html
      /body/form/div[5]/div/div/div/div[2]/div/table/tbody/tr[2]/td[3]/span”).GetROProperty(“innertext”)

    • unmesh says:

      I think you need to correct this syntax:

      datatable.Value(“Prices”) = Browser(“*Browser*”).Page(“*Page*”).WebElement(“xpath:=/html
      /body/form/div[5]/div/div/div/div[2]/div/table/tbody/tr[2]/td[3]/span”).GetROProperty(“innertext”)

      Should work now!!

      Best,
      Unmesh

  3. Mema says:

    how does QTP automtically record xpath? You mentioned there is a feature that needs to be enabled in Tools/Options. But I am not finding the location.

    Please help!

  4. Sujay says:

    Hi Unmesh!!

    Very good article to understand “XPath/CSS” and explanation of xpath/CSS is simple i.e.every one can understand..Keep posting new articles.

    Thanks!
    Sujay

  5. Reena says:

    Good article very useful in real time scenarios. Thanks:)

  6. Reena says:

    I had faced the same issue in QTP 10 which made me fetch table data making the script complicated.

  7. roli says:

    it is a good article .it helped me a lot.sir i have a doubt why you use the desendant in the example you given. cannot the task of finding the cell” quantity” be achived only using following.sibbling.

    • unmesh says:

      Hi Roli,

      Thanks a lot for your comments.

      Since the DIV and INPUT elements rendered inside Table’s Cell I have used descendent. The following.sibling won’t work in this case.

      Best,
      Unmesh

  8. Manaysah says:

    extremely helpful … thanks a lot 😉

  9. Sanjay Jain says:

    Thanks for this Article . Keep posting such articles.

  10. madhu says:

    Good one still needs more examples

  11. Martin says:

    I know this if off topic but I’m looking into starting
    my own blog and was curious what all is required to get setup?
    I’m assuming having a blog like yours would cost a pretty
    penny? I’m not very internet smart so I’m not 100% certain. Any tips or advice
    would be greatly appreciated. Appreciate it

  12. using a pill to help with baldness says:

    Fastidious response in return of this query with solid arguments
    and describibg the whole thing about that.

  13. Sofia says:

    Its noot my first time to go to see this website, i am visiting this website dailly and
    obtain nice facts frdom here every day.

  14. Good replies in return of this query with firm arguments and explaining tthe whole thing concerning that.

  15. Irfan says:

    Very good explanation

  16. Priyanka says:

    Which is more efficient mechanism for Object Identification in QTP – Xpath or Ordinal Identifiers?

    • varun says:

      xpath is used to identify the object uniquely

    • Joel says:

      I somewhat disagree with varun. A little effort into adding unique identifiers will go a long way towards stability of your tests. Many times position in the dom will change but identifier and verification points I’ll remain the same. In which case using findelement (by.id(“[id]”) would still pass.

  17. varun says:

    Hi my script contains xpath like this when I am running it is getting syntax error. can any 1 guide where is the problem
    DwnArrow= Browser(“brw_CIS2”).Page(“pge_Settings – Data Fields”).WebElement(“xpath:= //DIV[@id=”content”]/DIV[2]/NG-VIEW[1]/CIS2-TECHNICAL-DATA[1]/DIV[1]/DIV[1]/DIV[3]/CIS2-TD-DATA-FIELD-TREE-STRUCTURE[1]/DIV[2]/DIV[2]/SPAN[5]/A[1]/I[1]”).fireevent “onmouseover”


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