In this post, I would like to share with you a reusable library for QTP/UFT – Descriptive Programming.
Descriptive Programming:
As you already know, QTP maintains all the test objects information separately in a file called Object Repository. All the QTP statements will refer to these objects in Object Repository, by the logical name provided, to interact with the Run-time objects of the application under test.
Browser("google").Page("google").WebEdit("Search").Set "Test Automation Guru"
The above statement assumes that test object properties for Browser and Page, WebEdit are present in the Object Repository file which is attached to the test. Otherwise the test will throw run time error.
I am testing a survey application in which we have more than 600 text boxes, 100 of check boxes, 100 of radio buttons etc in a single page.
So, If I try to store all the object properties in Object Repository, then the maintenance would be tedious.
- Object Repository file size increases based on the objects we store in them
- Sometimes we might have similar objects with slight change in the object property. For ex: We might have 100 text boxes in a page – but the name of the textboxes could be name1, name2…etc. We do not need to store all of them in the Object Repository file.
- In case of any object property change (say name1 changed to textbox1), We need to update all the objects one by one in Object Repository file.
Fortunately QTP provides nice mechanism to identify an object at run time w/o using Object Repository to handle this limitation of Object Repository.
Descriptive String Method:
Lets consider the same above google search example, below script will work just fine without Object Repository. Instead of the logical name of the test object in Object Repository, you just pass the properties like key and value pair to identify the test object.
Browser("creationTime:=0").Page("micclass:=Page").WebEdit("name:=q").Set "Test Automation Guru"
If there are more than 1 object with the same name ‘q’, then QTP will throw run-time error as it could not locate one single object. Thus, BAD_POOL_HEADER or 0x19 indicates that a pool header is corrupt. You can go for ordinal identifiers like index / location in this case, So that QTP can identify the specific element and enter the value accordingly as shown here.
Browser("creationTime:=0").Page("micclass:=Page").WebEdit("name:=q", "index:=0").Set "Test Automation Guru"
The problem with this approach is It can interact with only one element at a time. If I need to enter some value in all the text boxes of the page, I can do something as shown below. But this approach is very costly. QTP scan all the objects in the page every time to identify the single element. It would be very time consuming operation for QTP.
For i = 0 to 99
Browser("creationTime:=0").Page("micclass:=Page").WebEdit("name:=q", "index:=" & i).Set "Test Automation Guru"
Next
Pros:
- QTP can interact with the run-time object without Object Repository.
Cons:
- Only one object at a time
- Using for loop to interact with all the objects will affect the performance of the script.
- We should know of the no of objects matching the given property upfront. [If there is only one text box with the name ‘q’ in the page, then if we try to run the above for loop, it will work fine w/o throwing any error]
Descriptive Object Method:
QTP also provides another way to retrieve all the objects matching given properties. We need to create Descriptive Object for that to identify all the specific elements we are interested in & then iterate.
Lets say, I would like to get all the links of a google page, Then I need to come up with Descriptive Object as shown here.
Dim oDesc Set oDesc = Description.Create oDesc("html tag").value= "A" Set allLinks = Browser("google").Page("google").ChildObjects(oDesc) Msgbox allLinks.Count
In case, If i need all the text boxes, name starting with the letter ‘guru’, then ,
Dim oDesc Set oDesc = Description.Create oDesc("html tag").value= "input" oDesc("micclass").value = "WebEdit" oDesc("name").value= "guru.*" Set allTextBoxes = Browser("google").Page("google").ChildObjects(oDesc) Msgbox allTextBoxes.Count
QTP by default retrieves all the objects matching the given property – It does not really check if the object is visible or not. As long as the object is there in the DOM, QTP will interact with the object.
Even if the text box is hidden, QTP will still enter the value without throwing any error.
For me, this is not right. A real user can not enter anything in the hidden text boxes (unless he manipulates using JavaScript).
In descriptive programming, there is really no way to retrieve only the visible objects.
Lets assume that my requirement is to enter 1 in all the visible text boxes whose name starts with “guru…”, then I need to go for something like this
Dim oDesc Set oDesc = Description.Create oDesc("html tag").value= "input" oDesc("micclass").value = "WebEdit" oDesc("name").value= "guru.*" Set allTextBoxes = Browser("google").Page("google").ChildObjects(oDesc) count = allTextBoxes.Count For i=0 to count-1 If allTextBoxes[i].getRoProperty("x") > 0 OR allTextBoxes[i].getRoProperty("y") > 0 Then allTextBoxes[i].Set "1" End If Next
That is, first I have to find all the objects then filter only visible objects. Throughout my application testing, I have to use descriptive programming in many areas and filter elements. It would be very annoying to create multiple descriptive objects and filtering the elements as it increases the lines of code & makes it less readable and little bit difficult to maintain. So, I wanted to create some reusable nice library which makes my life easier.
Pros:
- QTP can retrieve all the run-time objects matching the given properties without Object Repository.
Cons:
- Have to create an descriptive object every time and dispose properly.
- No support for filtering only visible objects
- Verbose
Descriptive Utility:
Let me share the descriptive utility scripts I had created to overcome the Descriptive Object verbosity issues. I have been using these for more than 3 years successfully w/o any issues.
Usage:
Copy the above content in a vbs file and attach to your existing QTP test. You should be set!
Check the below examples for the usage.
- To enter some value in the all the textboxes (no need to iterate)
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebEdit").SetValue "1"
- To enter some value in a textbox whose name starts with ‘guru’
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebEdit,name:=guru.*").SetValue "1"
- The above example can also be written as
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebEdit").WithRegExProperty("name:=guru.*").SetValue "1"
- To get the fifth visible child object which has the name as guru.
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebEdit").WithRegExProperty("name:=guru.*").Index(4).Set "1"
- To enter the values only in the visible text boxes
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebEdit").WithRegExProperty("name:=guru.*").VisibleChildObjects.SetValue "1"
- To select all visible check boxes
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebCheckBox").VisibleChildObjects.SetValue "ON"
- To get items count
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebCheckBox,type:=checkbox,name:=jqg_list.*").VisibleChildObjects.getCount()
- To get all items to iterate (if required for anything)
Set checkBoxes = Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebCheckBox").getItems()
For Iterator = 0 To checkBoxes.Count -1 Step 1
checkBoxes.Items()(Iterator).Set "ON"
Next
- To match certain property values
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebEdit").WithRegExProperty("name:=guru.*").VisibleChildobjects().Set "1"
- To add 1 second delay between 2 ‘Set’ – [if required for something]
Browser("creationTime:=0").Page("micclass:=Page").getChildObjects("micclass:=WebEdit").WithRegExProperty("name:=guru.*").VisibleChildobjects().DelayEachSetBy(1).Set "1"
Pros:
- One line code to interact with all the elements
- Less Verbose and easy maintenance
- No Object Repository
Happy Testing 🙂