releavantcodes

120
Eval Function & Execute Statement : This topic covers the concepts of the Eval function and Execute statement in depth. The function Eval is used to evaluate a string and return the result of the string expression. The Execute statement on the other hand, executes one or more statements in a string expression and enables creation of dynamic code. Multiple statements can be executed at once by using colons or line breaks to separate them. If you have had a chance to use RelevantCodes[1]One and went over the function libraries, you would find that all the events are controlled through Execute statement – data is retrieved from the Excel Table and parsed by the function libraries into string statements. Once parsed, these string statements are then executed in the same manner as QTP code. Eval Syntax : Eval(expression) Execute Syntax : Execute statement Difference between Eval & Execute : In the syntax above, both expression and statement arguments are string statements. Both expressions can be derived from VBScript or QTP code; the difference lies in the fact that Eval will always return the result of the string evaluation whereas Execute will execute a string statement for execution and will not retrieve the result (but there are workarounds). The following example demonstrates the main difference between the 2 functions: x = 9 y = 10 bIsEqual = Eval("x = y") Execute "x = y" MsgBox "bIsEqual: " & bIsEqual MsgBox "X is no longer 9. It is: " & x Eval Function Execute Statement Note that Eval returned a Boolean value whereas Execute function performed a variable assignment. This article will cover the usage of both techniques in depth through both VBS and QTP code.

Upload: sanjay-sahu

Post on 15-Oct-2014

143 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Releavantcodes

Eval Function & Execute Statement : This topic covers the concepts of the Eval function and Execute statement in depth. The function Eval is used to evaluate a string and return the result of the string expression. The Execute statement on the other hand, executes one or more statements in a string expression and enables creation of dynamic code. Multiple statements can be executed at once by using colons or line breaks to separate them. If you have had a chance to use RelevantCodes[1]One and went over the function libraries, you would find that all the events are controlled through Execute statement – data is retrieved from the Excel Table and parsed by the function libraries into string statements. Once parsed, these string statements are then executed in the same manner as QTP code.

Eval Syntax : Eval(expression) Execute Syntax : Execute statement

Difference between Eval & Execute : In the syntax above, both expression and statement arguments are string statements. Both expressions can be derived from VBScript or QTP code; the difference lies in the fact that Eval will always return the result of the string evaluation whereas Execute will execute a string statement for execution and will not retrieve the result (but there are workarounds). The following example demonstrates the main difference between the 2 functions:

x = 9y = 10 bIsEqual = Eval("x = y")Execute "x = y" MsgBox "bIsEqual: " & bIsEqualMsgBox "X is no longer 9. It is: " & x

Eval Function Execute Statement

Note that Eval returned a Boolean value whereas Execute function performed a variable assignment. This article will cover the usage of both techniques in depth through both VBS and QTP code. You must be wondering why the syntax for Eval and Execute is different. The reason is that, Eval is a function. Execute is not. That means, the following statement for Eval: bIsEqual = Eval("x = y"), when written for Execute will return a Null value for bResult (but assignment will be performed for x):

bResult = Execute("x = y")MsgBox bResultMsgBox "x: " & x

Null Output X = 10

Page 2: Releavantcodes

With that said, let me share the workaround that can be used with Execute to return the evaluation just like we did with Eval. Here’s how:

x = 9y = 10 Execute "z = (x = y)"MsgBox "z: " & z

Return Z (False)

More on this concept will be covered in the section Evaluating Statements.

Creating Variables : Let’s assume a scenario where you need to create the following variables: var_1, … , var_5. One way is to manually create the variables:

var_1 = 1 : var_2 = 2 : var_3 = 3 : var_4 = 4: var_5 = 5

A quicker, and more dynamic way to achieve the same is through Execute statement:

For ix = 1 to 5 Execute "var_" & ix & " = " & ixNext

Option Explicit : Using the Execute statement also has a direct impact if the Option Explicit statement is ON. If a variable is not explicitly declared, an error will be occurred:

Option Explicit iNumber = 9

Run Error

When the same variable is created through Execute, the error will not occur and the variable will be created and value assigned successfully.

Option Explicit Execute "iNumber = 9"

Page 3: Releavantcodes

Evaluating Statements : Instead of equating variables and values, we will evaluate statements using Eval. A quick example using IsObject is demonstrated below:

Set dicObject = CreateObject("Scripting.Dictionary") If Eval("IsObject(dicObject)") Then MsgBox "Object created." Set dicObject = NothingEnd If

Similarly, the above code can be broken into a Select Case block and variables can be evaluated by their type:

Sub RetType(ByVal var)Select Case Eval("TypeName(var)")

Case "Integer" : MsgBox "Integer"Case "Dictionary" : MsgBox "Dictionary"Case "Nothing" : MsgBox "Nothing"Case "String" : MsgBox "String"

End SelectEnd Sub Call RetType(Nothing)Call RetType("Test")Call RetType(CreateObject("Scripting.Dictionary"))Call RetType(1)

Upon execution, the following results will be retrieved:

RetType(Nothing) RetType('Test') RetType(CreateObject) RetType(1)

The same function above can be written through Execute. However, with Execute, we cannot directly retrieve the result (it is not a function, unlike Eval). Therefore, we will create a variable and store the result with the variable; the variable will then be used as the Select Case condition.

Sub RetType(ByVal var)Dim bResult

 Execute "bResult = TypeName(var)"

 Select Case bResult

Case "Integer" : MsgBox "Integer"Case "Dictionary" : MsgBox "Dictionary"Case "Nothing" : MsgBox "Nothing"Case "String" : MsgBox "String"

Page 4: Releavantcodes

End SelectEnd Sub Call RetType(Nothing)Call RetType("Test")Call RetType(CreateObject("Scripting.Dictionary"))Call RetType(1)

In short then, with Execute, bResult is represented within the string statement:

Execute "bResult = TypeName(var)"

With Eval, bResult is an actual variable that holds the result like below:

bResult = Eval("TypeName(var)")

Creating Functions : I have seen this approach rarely used, but its important to know all the possibilities nonetheless. Yes, we can create Functions (really!) at run-time, as long as everything is a string and new lines are separated by colons or a line feed. This approach is generally fine for small functions, but it can get really confusing and hard to debug as the lines of code increase.

sFunc = "Function ExecuteTest"sFunc = sFunc & vbLfsFunc = sFunc & "MsgBox ""ExecuteTest executed."""sFunc = sFunc & vbLfsFunc = sFunc & "End Function" Execute sFunc Call ExecuteTest()

Function ExecuteTest()

We can write the same code above using colons (:) as well:

Execute "Function ExecuteTest : MsgBox ""ExecuteTest executed."" : End Function" Call ExecuteTest()

Executing QTP Statements : This is where the Execute statement absolutely shines. As stated before, RelevantCodes[1]One executes all keywords through Execute statement. It retrieves keywords from the Excel Table as strings at runtime, creates the relevant hierarchies, and executes the hierarchies with chosen events. The concept here and the concepts covered by previous examples will remain the same: we create action strings and execute them. The last

Page 5: Releavantcodes

example in this topic will show how different strings can be joined together to implement events on supplied test-objects (as strings). Let’s start with a quick example to check if our target browser exists:

strBrowser = "Browser(""title:=Google"")"

 Execute "MsgBox " & strBrowser & ".Exist(0)"

The above code can also be written with Eval as well, enabling us to retrieve the value directly instead of using the workaround approach if we were to use Execute:

strBrowser = "Browser(""title:=Google"")" bExist = Eval(strBrowser & ".Exist(0)")

The above statement when executed does the same operation as it would when you would verify the browser existence using an inline DP statement: Msgbox Browser("title:=Google").Exist(0). Let’s consider another example where we create an event on a WebEdit object by creating an object hierarchy through strings at run time.

strBrowser = "Browser(""title:=Google"")" 'Browser("title:=Google")strPage = "Page(""title:=Google"")" 'Page("title:=Google")strText = "WebEdit(""name:=q"")" 'WebEdit("name:=q")strEvent = "Set ""Execute Test""" 'Set "Execute Test"

Execute strBrowser & "." & strPage & "." & strText & "." & strEvent

It does look interesting, doesn’t it? The same approach can be used if you are using Object Repository instead of Descriptive Programming; simply replace the Programmatic Descriptions with the Logical Descriptions in your OR, parse everything in a string and Execute it!

The example below shows how QTP code can be executed into succession through strings. The last 4 statements show how all strings are combined together to 1. Enter the search terms, 2. Click Search Button, 3. Synchronize and 4. Navigate back to the Google search page.

strBrowser = "Browser(""title:=.*Google.*"")" 'Browser("title:=Google")strPage = "Page(""title:=.*Google.*"")" 'Page("title:=Google")strText = "WebEdit(""name:=q"")" 'WebEdit("name:=q")strButton = "WebButton(""value:=Google Search"")" 'WebButton("value:=Google Search")strEvent = "Set ""Execute Test""" 'Set "Execute Test"strClick = "Click" 'ClickstrSync = "Sync" 'SyncstrBack = "Back" 'Back

Execute strBrowser & "." & strPage & "." & strText & "." & strEventExecute strBrowser & "." & strPage & "." & strButton & "." & strClickExecute strBrowser & "." & strSyncExecute strBrowser & "." & strBack

The above strings are equivalent to the following QTP code:

Page 6: Releavantcodes

Browser("title:=.*Google.*").Page("title:=.*Google.*").WebEdit("name:=q").Set "Execute Test"Browser("title:=.*Google.*").Page("title:=.*Google.*").WebButton("value:=Google Search").ClickBrowser("title:=.*Google.*").SyncBrowser("title:=.*Google.*").Back

Closing Remarks : I hope this article has covered most of the common (and uncommon) uses of Eval and Execute. I’m sure you’ll find areas in your framework where these 2 techniques will make things cleaner and more dynamic. If there is something I have omitted, please feel free to share it with us. I hope you guys find this article useful :)

Regular Expressions and Select Method (ListBox) This post describes a few ways to use Regular Expressions to select the target item from List objects. All examples of this post demonstrate techniques to be used in Web Applications, but they can be easily extended to technologies that store all list items with delimiters.

Regex Select Using QTP Methods : The below method uses QTP’s technique of retrieving the all items property from the WebList. It also uses RegExp object, which means, regular expressions will be supported. If the pattern does not match the pattern or the exact string from the List, the first item will be selected by default. If the default selection does not fit your requirement, please feel free to modify the code as per your needs.

Function RegexSelectQTP(Object, sPattern)Dim oRegExp, arrAllItems, ix

 'Create RegExp ObjectSet oRegExp = New RegExpoRegExp.IgnoreCase = FalseoRegExp.Pattern = sPattern

 'Split Object's all_items propertyarrAllItems = Split(Object.GetROProperty("all items"), ";")For ix = LBound(arrAllItems) To UBound(arrAllItems)

'If RegExp pattern matches list item, we're done!If oRegExp.Test(arrAllItems(ix)) Then

Object.Select "#" & ixSet oRegExp = NothingExit Function

End IfNext

 'Select Item #1 by defaultObject.Select "#0"

End FunctionRegisterUserFunc "WebList", "RegexSelectQTP", "RegexSelectQTP"

Usage: Browser("").Page("").WebList("").RegexSelectQTP "London - Heathrow" 'Select London-Heathrow

Page 7: Releavantcodes

Browser("").Page("").WebList("").RegexSelectQTP "London - Heath.*" 'Select London-HeathrowBrowser("").Page("").WebList("").RegexSelectQTP "London - \D+" 'Select London-Heathrow

Regex Select Using DOM Methods : This is quite similar to above, but instead of using the all items property, it uses the HTML DOM options. In general, the HTML of a WebList is like this:

<select><option>Acapulco</option><option>London</option><option>New York</option><option>San Francisco</option>

</select>

The code below retrieves all the items within the option /option tag and loops through each to determine if there is a regexp match. Also, like the above method, if a match is not found, the first item in the list will be selected by default. Note: This method will not work for technologies other than Web.

Function RegexSelectDOM(Object, sPattern)Dim oRegExp, oOptions, ix

 'Create RegExp ObjectSet oRegExp = New RegExpoRegExp.IgnoreCase = FalseoRegExp.Pattern = sPattern

 'DOM optionsSet oOptions = Object.Object.OptionsFor ix = 0 to oOptions.Length - 1

'If RegExp pattern matches list item, we're done!If oRegExp.Test(oOptions(ix).Text) Then

Object.Select "#" &amp; ixSet oRegExp = NothingExit Function

End IfNext

 'Select Item #1 by defaultObject.Select "#0"

End FunctionRegisterUserFunc "WebList", "RegexSelectDOM", "RegexSelectDOM"

Usage: Browser("").Page("").WebList("").RegexSelectDOM "London - Heathrow" 'Select London-Heathrow

Browser("").Page("").WebList("").RegexSelectDOM "London - Heath.*" 'Select London-HeathrowBrowser("").Page("").WebList("").RegexSelectDOM "London - \D+" 'Select London-Heathrow

Page 8: Releavantcodes

Select Using VBScript (InStr/Mid) Methods : This method does not support Regular Expressions as it is based on VBScript’s InStr and Mid methods. Instead, it parses the correct value depending upon the supplied complete or partial values without the regexp meta characters.

Public Function VBSSelect(Object, sString)Dim sAllItems, varLocation, varEnd, varBeginning

 'Retrieve Object's all_items propertysAllItems = Object.GetROProperty("all items")

 'Verify if the supplied string is found in list's all_items propertyvarLocation = InStr(1, sAllItems, sString)'If found:If varLocation &gt; 0 Then

varEnd = InStr(varLocation, sAllItems, ";")If varEnd = 0 Then varEnd = Len(sAllItems) + 1varBeginning = InStrRev(sAllItems, ";", varLocation)Object.Select "" &amp; Mid(sAllItems, varBeginning + 1, varEnd

- varBeginning - 1)Exit Function

End If 

'Select Item #1 by defaultObject.Select "#0"

End FunctionRegisterUserFunc "WebList", "VBSSelect", "VBSSelect"

Usage: Browser("").Page("").WebList("").VBSSelect "London - Heathrow" 'Select London-Heathrow

Browser("").Page("").WebList("").VBSSelect "London - Heath" 'Select London-HeathrowBrowser("").Page("").WebList("").VBSSelect "London - " 'Select London-Heathrow

Performance Comparison : The above 3 methods were tested on QTP 10 against a Web Application with 500 iterations each on a Dell XPS M1210 Core2Duo 2.0GHz 3GB Laptop. The table below presents the average time taken by each method to successfully select a value from the WebList in QTP’s Normal and Fast run modes:

Performance Comparison between Select MethodsRun Mode Normal Fast

RegexSelectQTP 0.44 seconds 0.38 secondsRegexSelectDOM 0.45 seconds 0.40 seconds

VBSSelect 0.39 seconds 0.35 seconds

VBScript: Compare 2 Excel Files : This post shows a quick and easy way to compare 2 Excel files. I know there are several code snippets on forums that show how to compare do this, so you may ask the following question: how is this technique any different? What makes this technique different is performance. I know there may be faster ways to achieve this, but after

Page 9: Releavantcodes

much experiment, this was the one that continuously worked at the best speed in comparison to other methods. The main concept of this technique is performing all validations through a range object, instead of traversing all cells of an Excel sheet (which takes a little longer). Below is the skeleton of how the class is arranged, with documentation. Please use one of the download links above to download this class. There is a usage example shown at the end of this article.

Class clsExcelComparer

Class clsComparer'[--- Region Private Variables Start ---]

Private oExcel 'Excel.ApplicationPrivate arrRangeUno 'Range.Value (array) of the Primary Excel

spreadsheetPrivate arrRangeDos 'Range.Value (array) of the Secondary Excecl

spreadsheetPrivate oDict 'Scripting.Dictionary containing unmatched

cells

'[--- Region Private Variables End ---]

 '[--- Region Public Variables Start ---]

Public Operation '0: Only Compare 1: Compare & Highlight Differences

'[--- Region Public Variables End ---]

 '--------------------------------------------------------' Name: Function Compare [Public]'' Remarks: N/A'' Purpose: Compares differences between 2 Excel Spreadsheets'' Arguments:' sWorkBookUno: Primary Excel WorkBook (with complete path)' vSheetUno: Primary Excel Spreadsheet Name' sWorkBookDos: Secondary Excel WorkBook (with complete path)' vSheetDos: Secondary Excel Spreadsheet Name'' Return: Boolean'' Author: Anshoo Arora, Relevant Codes'' Date: 03/17/2010'' References: N/A'--------------------------------------------------------Public Function Compare(sWorkBookUno, vSheetUno, sWorkBookDos,

vSheetDos) 

End Function

Page 10: Releavantcodes

 '--------------------------------------------------------' Name: Function CellsFound [Private]'' Remarks: N/A'' Purpose: Finds the dissimilar cells between 2 sheets'' Arguments: N/a'' Return: Integer'' Author: Anshoo Arora, Relevant Codes'' Date: 03/17/2010'' References: N/A'--------------------------------------------------------Private Function CellsFound()

 End Function

 '--------------------------------------------------------' Name: Sub Class_Terminate [Private]'' Remarks: N/A'' Purpose: Disposes the Excel.Application object'' Arguments: N/A'' Author: Anshoo Arora, Relevant Codes'' Date: 03/17/2010'' References: N/A'--------------------------------------------------------Private Sub Class_Terminate()

 End Sub

 End Class '--------------------------------------------------------' Name: Function CompareExcelSheets'' Remarks: N/A'' Purpose: Constructor for Class clsComparer'' Arguments:' sWorkBookUno: Primary Excel WorkBook (with complete path)' vSheetUno: Primary Excel Spreadsheet Name' sWorkBookDos: Secondary Excel WorkBook (with complete path)' vSheetDos: Secondary Excel Spreadsheet Name' Operation: 0: Compare Only 1: Compare & Highlight Differences'

Page 11: Releavantcodes

' Return: Boolean'' Author: Anshoo Arora, Relevant Codes'' Date: 03/17/2010'' References: N/A'--------------------------------------------------------Function CompareExcelSheets(sWorkBookUno, vSheetUno, sWorkBookDos, vSheetDos, Operation) End Function

Usage (Example)

Consider the following 2 Excel Sheets, with a few dissimilar cells:

Sheet #1 Original

Sheet #2 Original

There are 2 main operations that can be done through our code. 1. We can simply check if both Excel Sheets are similar and 2. we can check as well as highlight the differences between the two. To only compare the 2 sheets, without highlighting the differences the Operation argument will have a value of 0. On the other hand, to compare as well as highlight the values, the Operation argument will have a value of 1. Let’s first consider a case where the 2 sheets are only to be compared:

Compare Only :

'Only Compare: MsgBox CompareExcelSheets("C:\Test1.xls", "Sheet1", "C:\Test2.xls", "Sheet1", 0)

Page 12: Releavantcodes

Because the 2 sheets are not similar, the following will be the output upon execution of the above statement:

Compare Only

Also, the 2 sheets will remain unchanged.

When changing the value of Operation to 1, we will notice that, not only does the code compare the 2 sheets, but also highlights the differences.

Compare and Highlight Differences

'Compare and highlight differences:MsgBox CompareExcelSheets("C:\Test1.xls", "Sheet1", "C:\Test2.xls", "Sheet1", 1)

Upon execution of the above statement, the following 2 sheets will be highlighed in areas where they differ:

Sheet #1 With Highlighted Differences

Sheet #2 with Highlighted Differences

Limitations : 1. This code will work with Excel Sheets with the same number of rows and columns in

each (like ranges).

Page 13: Releavantcodes

Update: RelevantCodes.Comparer Assembly :

To register the assembly to your PC, the following code can be used:

RegAsm C:\RelevantCodes.dll /codebase

or

C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe C:\RelevantCodes.dll /codebase

Once done, the code can be used in QTP like this:

Set oCompare = CreateObject("RelevantCodes.Comparer")

oCompare. should give you the proper intellisense to use all methods. The assembly compares the following items:

1. Arrays 2. Excel Sheets 3. Images 4. Integers 5. Strings 6. TextFiles

I hope you find this useful :)

QTP Browser Methods: .Activate .Maximize .Minimize

This article demonstrates a quick tip to activate, minimize or maximize browsers. Unlike a Standard Windows Window object, Browser does not support the Activate, Minimize, Maximize methods. Therefore, we can create our custom function and tie it with the Browser object using RegisterUserFunc. The only trick is to retrieve the Browser Handle and substitute the handle in the description of a Window object, and use the Window’s Activate method instead.

Activate Browsers:

Function BrowserActivate(Object)Dim hWnd

 hWnd = Object.GetROProperty("hwnd")

 On Error Resume Next

Window("hwnd:=" & hWnd).Activate 

If Err.Number <> 0 ThenWindow("hwnd:=" & Browser("hwnd:=" &

hWnd).Object.hWnd).Activate

Page 14: Releavantcodes

Err.ClearEnd If

On Error Goto 0End Function RegisterUserFunc "Browser", "Activate", "BrowserActivate"

After registering the BrowserActivate function with the Browser object as Activate we can use it just like we would use it for a Window object:

Browser("title:=Relevant Codes.*").Activate

BrowserActivate can be extended to maximize and minimize a browser window as well. The only extra statement to be included in the function would be the maximize and minimize methods of the window object.

Minimize Browsers:

Function BrowserMinimize(Object)Dim hWnd

 hWnd = Object.GetROProperty("hwnd")

 On Error Resume Next

Window("hwnd:=" & hWnd).Activate 

If Err.Number <> 0 ThenhWnd = Browser("hwnd:=" & hWnd).Object.hWndWindow("hwnd:=" & hWnd).ActivateErr.Clear

End If 

Window("hwnd:=" & hWnd).MinimizeOn Error Goto 0

End Function RegisterUserFunc "Browser", "Minimize", "BrowserMinimize"

Maximize Browsers:

Function BrowserMaximize(Object)

Dim hWnd 

hWnd = Object.GetROProperty("hwnd") 

On Error Resume NextWindow("hwnd:=" & hWnd).Activate

 If Err.Number <> 0 Then

hWnd = Browser("hwnd:=" & hWnd).Object.hWndWindow("hwnd:=" & hWnd).ActivateErr.Clear

End If

Page 15: Releavantcodes

 Window("hwnd:=" & hWnd).Maximize

On Error Goto 0End Function RegisterUserFunc "Browser", "Maximize", "BrowserMaximize"

If you would like to use the above 3 methods through a single function or class, they can be coupled together through Execute statements or through If-Then or Switch-Case blocks. Happy reading!

QTP: Parameterization with Excel In the previous article, we parameterized a test for a Login process with the help of QTP’s Local DataTable. This topic covers the same concept with the help of an (external) Excel file. The process will remain the same, but how we extract data differs. If you are looking to understand Parameterization concepts and need a quick introduction, I would recommend you to read the following article: Introduction to Parameterization with QTP’s Local DataTable . Before we start the process of extracting data from Excel, and using the extracted data in our automated process, we must first understand the Excel Automation object. Before that even, we must understand the CreateObject function. The CreateObject function creates and returns a reference to an Automation object1. In other words, usage of CreateObject in terms of Excel will return the reference of Excel’s Automation object. This reference will enable us to use Excel’s methods for data retrieval. Below is the syntax of CreateObject:

Set Var = CreateObject(servername.typename [, location])

The Set keyword is used here because we have to bind out variable to an object. The Set statement assigns an object reference to a variable or property, or associates a procedure reference with an event2. We can use the syntax above in terms of Excel and create a reference object that will hold methods of the Excel application:

Dim xlApp 'Excel Application Automation Object Reference

Set xlApp = CreateObject("Excel.Application")

Next, we will create a reference to the Excel WorkBook, which is the file that we will open to retrieve data from. The workbook we’re working with is located in the root folder: C:\ and its name is TestFile.xls. Thus, we will substitute the pathName\fileName in our code below:

Dim xlBook 'Reference to the workbook located in C:\

'Set varExcelWorkBook = ExcelObjectReference.WorkBooks.Open("File")Set xlBook = xlApp.WorkBooks.Open("C:\TestFile.xls")

Next, let’s create a reference to the Excel Spreadsheet (Sheet1, Sheet2, Sheet3..) which contains the data we need to drive our parameterized script. Our data is contained in Sheet1 of our workbook, thus, we will use it in our code below:

Dim xlSheet 'Reference to Sheet1

Page 16: Releavantcodes

'Set varExcelWorkSheet = varExcelWorkBook.WorkSheets("Sheet")Set xlSheet = xlBook.WorkSheets("Sheet1")

Below is a snapshot of the spreadsheet, with headings in Row 1 and data starting from Row 2:

To retrieve the number of rows and columns used in the table, we will use the code below:

'Total RowsiRows = xlSheet.UsedRange.Rows.Count 'Total ColumnsiCols = xlSheet.UsedRange.Columns.Count

Lastly, we will retrieve the data from Excel:

'First UserName is stored in the first column (Column A), second Row (A2) of the spreadsheetsUserName = xlSheet.Rows(2).Columns(1).Value 'First Password is stored in the second column (Column B), second Row (B2) of the spreadsheetsPassword = xlSheet.Rows(2).Columns(2).Value

I think we have covered the concepts needed to parameterize our script directly from an Excel file. Now, let’s use the same example we did in the previous article to parameterize the Login process for Mercury/HP’s demo AUT.

1. Launch application2. Enter UserName/Password from DataTable and click Login3. Verify if the Find Flights page appears4. If Find Flights page appears, iteration passed.5. Return to Home Page and start Step #2'Step 1: Launch ApplicationSystemUtil.Run "iexplore.exe", "http://newtours.demoaut.com"'Step 2: Enter UserName/Password from DataTable and click Login

'Retrieve UserName and Password from "iRow" rows and columns A & BsUserName = xlSheet.Rows(iRow).Columns(iUserNameCol).ValuesPassword = xlSheet.Rows(iRow).Columns(iPasswordCol).Value 'Parameterization Block:With Browser("title:=Welcome: Mercury Tours", "index:=0")

.WebEdit("name:=userName").Set sUserName 'Parameter 1: UserName

.WebEdit("name:=password").Set sPassword 'Parameter 2: Password

.Image("name:=login").ClickEnd With

Page 17: Releavantcodes

'Step 3: Verify if the Find Flights page appears

If Browser("title:=Find a Flight: Mercury Tours:", "index:=0").Exist(10) Then'code

End If'Step 4: If Find Flights page appears, iteration passed.

If Browser("title:=Find a Flight: Mercury Tours:", "index:=0").Exist(10) Then Reporter.ReportEvent micPass, "Iteration " & iRow-1, "UserName: " & sUserName & " is valid"Else Reporter.ReportEvent micFail, "Iteration " & iRow-1, "UserName: " & sUserName & " is invalid"End If'Step 5: Return to Home Page and start Step #2

If Browser("title:=Find a Flight: Mercury Tours:", "index:=0").Exist(10) Then Browser("title:=Find a Flight: Mercury Tours:", "index:=0").Link("text:=Home").Click Reporter.ReportEvent micPass, "Iteration " & iRow-1, "UserName: " sUserName & " is valid"Else Reporter.ReportEvent micFail, "Iteration " & iRow-1, "UserName: " &sUserName& " is invalid" Browser("title:=Sign-on: Mercury Tours", "index:=0").Link("text:=Home").ClickEnd If

Steps 1-5 We can now combine all the 5 steps into a single code block:

'Steps 2-5

Dim xlApp, xlBook, xlSheetDim iRow, sUserName, sPasswordCONST iUserNameCol = 1 'UserName is in Column ACONST iPasswordCol = 2 'Password is in Column B

Set xlApp = CreateObject("Excel.Application")Set xlBook = xlApp.WorkBooks.Open("C:\TestFile.xls")Set xlSheet = xlBook.WorkSheets("Sheet1") 'iRow = 2 because data to be driven starts from Row #2For iRow = 2 to xlSheet.UsedRange.Rows.Count  'Retrieve UserName and Password from "iRow" rows and columns A & B sUserName = xlSheet.Rows(iRow).Columns(iUserNameCol).Value sPassword = xlSheet.Rows(iRow).Columns(iPasswordCol).Value  'Parameterization Block: With Browser("title:=Welcome: Mercury Tours", "index:=0") 'Step 2 .WebEdit("name:=userName").Set sUserName 'Parameter 1: UserName .WebEdit("name:=password").Set sPassword 'Parameter 2: Password .Image("name:=login").Click End With  

Page 18: Releavantcodes

'Step 3: If Find a Flight page appears, go back to Home If Browser("title:=Find a Flight: Mercury Tours:", "index:=0").Exist(10) Then Browser("title:=Find a Flight: Mercury Tours:", "index:=0").Link("text:=Home").Click  'Step 4: Pass the iteration Reporter.ReportEvent micPass, "Iteration " & iRow-1, "UserName: " &sUserName& " is valid" Else 'Step 5: Fail the iteration and return to the Home page Reporter.ReportEvent micFail, "Iteration " & iRow-1, "UserName: " &sUserName& " is invalid" Browser("title:=Sign-on: Mercury Tours", "index:=0").Link("text:=Home").Click End If  NextxlBook.ClosexlApp.QuitSet xlSheet = NothingSet xlBook = NothingSet xlApp = Nothing

When the above code executes, the follow results will be populated in QTP:

Parameterization with Excel: Results

I hope this article will help automation developers create parameterization with Excel. The concepts covered in this article are quite basic, but they should serve as a baseline to creating parameterization through Excel which can give users more control over how they data-drive their tests. In the coming articles, I will show how Excel can be used as a data-source with performance that QTP’s DataTable just doesn’t provide. I hope you find this useful :)

Page 19: Releavantcodes

GetROProperties (Custom) There have been numerous threads/discussions about the lack of GetROProperties, which is not available with the QuickTest package. There is GetTOProperty, GetROProperty, GetTOProperties and until now, GetROProperties was the only method missing from the pack. Now, its available and it works just like GetTOProperties does. This article shows how the method is created, and there are 2 examples at the end of this article which demonstrate its usage. I would strongly recommend automation developers using this method also read the article RegisterUserFuncX: Register Methods To All QTP Objects Automatically, which can be used to register GetROProperties to all QuickTest Objects. To replicate the way GetTOProperties works, this method uses a RecordSet object to store the properties and their corresponding values. The descriptions of objects are retrived from the Registry, and because not all keys (properties) are present there, you would have to manually create the missing keys. The location of the registry where keys are retrieved from is:

HKEY_LOCAL_MACHINE\Software\Mercury Interactive\QuickTest Professional\MicTest\Test Objects\Object Class\Description

This method comes AS-IS and I have kept everything quite basic, leaving room for any sort of upgrades that may be required in your environment. However, this method does deliver most of the mandatory properties that would be required by the user anyways.

Class clsGetROProperties Private arrProperties Private sObject  Public Function GetProperties(Object) Dim arrObject, ix  Set GetProperties = Nothing  'Retrieve object type / Object Class sObject = Object.GetTOProperty("micclass")  'If Object is found in Registry then, add all ROProperties to RecordSet If bObjectExistsInReg Then Const adVarChar = 200  Set GetProperties = CreateObject("ADODB.RecordSet")  'Add Properties available in the Registry For ix = LBound(arrProperties) to UBound(arrProperties) Select Case LCase(arrProperties(ix)) Case "" Case Else GetProperties.Fields.Append "" & arrProperties(ix), adVarChar, 2000 End Select Next  'Open the RecordSet Connection for Write Access GetProperties.Open : GetProperties.AddNew  'Use .GetROProperty to retrieve and store all values in the RecordSet

Page 20: Releavantcodes

On Error Resume Next For ix = 0 to GetProperties.Fields.Count - 1 GetProperties.Fields(ix).Value = Object.GetROProperty(GetProperties.Fields(ix).Name) Next On Error Goto 0  GetProperties.Update End If End Function  Private Function bObjectExistsInReg() Dim oRegistry, arrSubKeys, sSubKey Const HKEY_LOCAL_MACHINE = &H80000002  bObjectExistsInReg = False  Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\." &_ "\root\default:StdRegProv")  oRegistry.EnumKey HKEY_LOCAL_MACHINE, GetKeyPath, arrSubKeys  For Each sSubKey In arrSubKeys 'If Object Class matches one of the Registry Sub-Keys, then, 'drill-down to the Description folder of the key If UCase(sSubKey) = Trim(UCase(sObject)) Then oRegistry.EnumValues HKEY_LOCAL_MACHINE, GetKeyPath & "\" & _ sSubKey & "\Description\", arrProperties bObjectExistsInReg = True Exit For End If Next  Set oRegistry = Nothing End Function  Private Property Get GetKeyPath() GetKeyPath = "SOFTWARE\MERCURY INTERACTIVE\QuickTest Professional\MicTest\Test Objects" End PropertyEnd Class Function GetROProperties(Object) Set GetROProperties = New clsGetROProperties Set GetROProperties = GetROProperties.GetProperties(Object)End Function

Below are 2 examples how GetROProperties can be used with different objects. I have manually registered the method for the objects. I would strongly recommend automation developers using this method also read the article RegisterUserFuncX: Register Methods To All QTP Objects Automatically, which can be used to register GetROProperties to all QuickTest Objects.

Example 1: 'RegisterUserFuncX "GetROProperties", "GetROProperties"

Page 21: Releavantcodes

RegisterUserFunc "WebEdit", "GetROProperties", "GetROProperties" Set oROProperties = Browser("title:=Google").WebEdit("name:=q").GetROProperties For ix = 0 to oROProperties.Fields.Count - 1

Print oROProperties(ix).Name & " -> " & oROProperties(ix).ValueNext Set oROProperties = Nothing

Once the snippet above executes, below is what the result would look like in QuickTest Print Log:

GetROProperties Result

Example 2: 'RegisterUserFuncX "GetROProperties", "GetROProperties"

RegisterUserFunc "Link", "GetROProperties", "GetROProperties" Set oROProperties = Browser("title:=Google").Link("innertext:=About Google").GetROProperties For ix = 0 to oROProperties.Fields.Count - 1

Print oROProperties(ix).Name & " -> " & oROProperties(ix).ValueNext Set oROProperties = Nothing

Once the snippet above executes, below is what the result would look like in QuickTest Print Log:

GetROProperties Result

Page 22: Releavantcodes

I hope this article will cover one of the methods that isn’t available with the shipped QuickTest Professional package. Please use the comments section of this article to provide your feedback, offer improvements to this method or report any bugs that you encountered in your environment.

RegisterUserFuncX: Register Methods To All QTP Objects Automatically

Let’s suppose we have a function called GetProperty, which we would like to register to all objects available to us. Now, if we do it manually, it can be quite time-consuming since any sort of AUT has many objects, and if you’re working in an environment where multiple technologies are involved, this can even get a little boring. Now, though, there is a simple way of register all our objects to the function by simply calling the method RegisterUserFuncX. Before we see how the method RegisterUserFuncX works, let’s create a function GetProperty function, which we will first manually register to the objects in our multi-technology (Standard Windows, Web, Siebel) AUT:

Function GetProperty(Object)GetProperty = Object.GetTOProperty("micclass")

End Function'WebRegisterUserFunc "Link", "GetProperty", "GetProperty"RegisterUserFunc "WebEdit", "GetProperty", "GetProperty"'Standard WindowsRegisterUserFunc "WinEditor", "GetProperty", "GetProperty"'SiebelRegisterUserFunc "SiebList", "GetProperty", "GetProperty"

Now, this is just an example. There are obviously many more objects I would have to register the function with. However, instead of doing this manually, I can simply use the below method and register any given function to all objects automatically:

Function RegisterUserFuncX(TOMethodName, FunctionName)Dim oRegistry, arrSubKeys, sSubKey, sKeyPathConst HKEY_LOCAL_MACHINE = &H80000002

 sKeyPath = "SOFTWARE\MERCURY INTERACTIVE\QuickTest Professional\

MicTest\Test Objects" 

Set oRegistry = GetObject("winmgmts:{impersonationLevel=impersonate}!\\." &_

"\root\default:StdRegProv") 

oRegistry.EnumKey HKEY_LOCAL_MACHINE, sKeyPath, arrSubKeys 

For Each sSubKey In arrSubKeysRegisterUserFunc sSubKey, TOMethodName, FunctionName

Next 

Set oRegistry = NothingEnd Function

Page 23: Releavantcodes

If we have to associate all objects in our AUT to the custom function, we would just call the above method once to do our job:

Call RegisterUserFuncX("GetProperty", "GetProperty")

This is the equivalent of registering the GetProperty method to all objects manually – which requires writing many individual RegisterUserFunc statements. The concept behind this method is that in the following folder of our Registry (regedit), we have all the objects stored:

HKEY_LOCAL_MACHINE\SOFTWARE\MERCURY INTERACTIVE\QuickTest Professional\MicTest\Test Objects

Therefore, instead of registering single object classes to our function, we can use the method RegisterUserFuncX and register any given function to all objects at once. The above function can also be tweaked to only register Web Objects, or Windows, or objects of an environment of your choice. This would involve manipulating the For..Each loop a bit:

'Register all the Web ObjectsFor Each sSubKey In arrSubKeys

If InStr(1, sSubKey, "Web") > 0 ThenRegisterUserFunc sSubKey, TOMethodName, FunctionName

End IfNext

Limitations

1. This technique doesn’t well as its supposed to in terms of Siebel Objects (See Tarun’s comment here). This is because the Siebel Objects in the Registry are named differently as opposed to how the Object Spy identifies them. Objects in the Registry are named as “Sbl..” as opposed to “Sieb..”, which is the main reason for this shortcoming.

The above limitation has been resolved: Certain objects are available in QTP’s DAT folder, and by modifying the actual code in this post for XMLs, all objects can still be registered easily to custom methods. Credits go to Motti for reporting this resolution.

2. Another limitation of this approach is due to the dynamically executing RegisterUserFunc statements, which end up in the lack of Intellisense during test-design. This can be overcome by using RegisterUserFuncX to print all the necessary RegisterUserFunc statements and pasting the output to our libraries. Full credit goes to Tarun for helping us find this limitation.

I hope this helps. Thanks for visiting Relevant Codes

QTP: Working With Multiple Windows Applications After writing the article Working with Multiple Browser Applications with QTP, I thought, there are several instances where automation developers have to work with applications containing multiple windows, in a Standard Windows Environment. This technique uses a similar methodology as demonstrated in

Page 24: Releavantcodes

the article for Web-based apps, but the crux of this technique differs. It can be used by automation developers testing most types of windows applications, and it contains concepts that are showcased in my generic automation framework RelevantCodes[1]One, which will be released in the coming few weeks.

With the help of this technique, regardless of how many windows applications are opened through QTP, we would never have to keep track of the Window’s title or any of its recognition properties (unless we want to). In other words, regardless of a window’s dynamic nature, this concept will enable you to give the window a name of your choice, and use that name to identify the window, instead of keeping track of changes in its properties.

What this concept contains:

LaunchAdd: Launch a new window and retain its reference throughout the test cycle AddNew: Automatically add a new open window to the collection without you having to

specify its properties (see list of dependencies) AddCustom: Adds a custom window by specifying its properties as an array All identifiers stay intact until the test finishes.

View clsWindow.txt | Download clsWindow.zip | Download Demo v9.2 | Download Demo v9.5

This concept contains 3 main methods, as described above. You can download the Class clsWindow for complete documentation, but for a quick overview, please continue reading.

First, and the one that may be used the most often is a method called AddCustom. It enables users to add a window of their choice to the collection, with the name they want to specify. From that point onwards, regardless of the number of changes that particular window goes through, it will not be required to update its properties. The name you give the window can be used throughout the test cycle. Below is the code snippet for AddCustom:

Public Sub AddCustom(arrPropertyValue, sName) Dim lngHwnd  Me.sName = sName Me.arrPropertyValue = arrPropertyValue  'Retrieve the handle of the specified window lngHwnd = GetCustomWindowHwnd()  'If the handle is valid, then add it to the global collection If lngHwnd <> -1 Then colObject.Add sName, Window("hwnd:=" & lngHwnd) If Not oDict.Exists(lngHwnd) Then oDict.Add lngHwnd, lngHwnd End If End IfEnd Sub

Page 25: Releavantcodes

Next is a method called LaunchAdd, which is dependent upon GetRecentlyOpenWindowHwnd. This will enable you to automatically add a newly launched window to the collection, with the name you specify:

Public Sub LaunchAdd(sFilePath, sName) Dim lngHwnd  Me.sFilePath = sFilePath Me.sName = sName  'Retrieve the window handle of the recently launched window lngHwnd = GetRecentlyOpenWindowHwnd()  'If the handle is valid, then add it to the global collection object If lngHwnd <> -1 Then colObject.Add sName, Window("hwnd:=" & lngHwnd) If Not oDict.Exists(lngHwnd) Then oDict.Add lngHwnd, lngHwnd End If End IfEnd Sub Private Function GetRecentlyOpenWindowHwnd() 'As Integer Dim sFilePath, sName, oDesc, oParent, mHwndDict, x, iTimeElapsed  sFilePath = Me.sFilePath sName = Me.sName  GetRecentlyOpenWindowHwnd = -1  On Error Resume Next  'Create a description object Set oDesc = Description.Create  'oParent holds references to all the open windows Set oParent = Desktop.ChildObjects(oDesc)  'Scripting.Dictionary: holds all open windows' handles Set mHwndDict = CreateObject("Scripting.Dictionary")  'Loop until all the handles are successfully added to the collection For x = 0 to oParent.Count - 1 mHwndDict.Add oParent(x).GetROProperty("hwnd"), x If Not oDict.Exists(oParent(x).GetROProperty("hwnd")) Then oDict.Add oParent(x).GetROProperty("hwnd"), oParent(x).GetROProperty("hwnd") End If Next  'Launch the target application SystemUtil.Run sFilePath  'Loop max 10 seconds for the window to open Do Set oParent = Desktop.ChildObjects(oDesc) For x = 0 to oParent.Count - 1 Select Case oParent(x).GetTOProperty("micclass")

Case "Window", "Dialog" If Not mHwndDict.Exists(oParent(x).GetROProperty("hwnd")) Then GetRecentlyOpenWindowHwnd = oParent(x).GetROProperty("hwnd") Exit Do End If

Page 26: Releavantcodes

End Select Next Wait(1) iTimeElapsed = iTimeElapsed + 1 Loop Until iTimeElapsed = 10   Set oDesc = Nothing Set oParent = Nothing Set mHwndDict = Nothing  On Error Goto 0End Function

Lastly, and a tricky one is AddNew. This will automatically add any new open window after LaunchAdd has been executed. Like the other 2 methods, this method will also enable preservation of a window’s property until the end of the test.

Public Sub AddNew(sName, iTimeOutBeforeWindowOpens) Dim oDesc, oParent, x, iTimeElapsed, lngHwnd  'Set default application timeout If iTimeOutBeforeWindowOpens = "" Then iTimeOutBeforeWindowOpens = 1 End If  'Create a Description Object Set oDesc = Description.Create  'oParent holds all open windows Set oParent = Desktop.ChildObjects(oDesc)  'Wait iTimeOutBeforeWindowOpens number of seconds 'Default iTimeOutBeforeWindow Opens = 1 Wait(iTimeOutBeforeWindowOpens)  'Loop for 5 seconds 'If within 5 seconds, a new unadded window is found, the window will ' be added to the collection and this procedure will end. Do 'Create a new collection Set oParent = Desktop.ChildObjects(oDesc)  'Loop for all objects in the collection and find a one that does 'not exist in the global dictionary: colWindows For x = 0 to oParent.Count - 1 If Not oDict.Exists(oParent(x).GetROProperty("hwnd")) Then lngHwnd = oParent(x).GetROProperty("hwnd") oDict.Add lngHwnd, lngHwnd colObject.Add sName, Window("hwnd:=" & lngHwnd) Exit Do End If Next Wait(1) iTimeElapsed = iTimeElapsed + 1 Loop Until iTimeElapsed = 5 'This timeout can be minized to 1, if desiredEnd Sub

Page 27: Releavantcodes

Technically, the concepts in this article can be easily transferred to any technology, just like the concepts were transferred from Web to Windows, which is quite a huge transition. Once this concept is implemented properly, it should help eliminate the need to constantly identify the windows we need to work with. I hope you find this helpful :)

Introduction to Parameterization with QTP’s Local DataTable Parameterization is a process in which parameters, or variables are substituted for hard-coded values which help perform the same operations on the AUT with multiple sets of data. In other words, in QTP, parameterization helps automation developers test an application with different data from a single action (or multiple actions) through multiple iterations. Because the data changes each iteration, it must be stored in a data-pool, or in multiple variables. This data-pool can be QTP’s DataTable, an Excel Spreadsheet (external to QTP), Access DB, SQL Server, or any database of your choice. It can also come from several Environment variables. This topic will describe parameterization with QTP’s DataTable. The trick to successful parameterization is cycling around a process. For example, consider a flight booking/checkout process. You would first login to the application, find and select the flight of your choice, make the purchase, and in the end view the confirmation. If you were to parameterize the above process, instead of ending your script at the confirmation page, you would end it at the Find Flight page. This is because, when the next iteration executes, it is already at the correct page which can enable the script to start all over again at the expected point. In other words, our process should look like below:

Parameterization with DataTable : Before we start with the actual parameterization, we must first understand how data will be retrieved from the DataTable and the settings that we need to make in order to cover all the rows. In our example, we will test a few UserName and Passwords in the HP/Mercury’s Demo AUT.

Below, I have created 2 columns in the Local (Action1) DataTable- UserName & Password. You can create columns by simply double-clicking the Column heading:

Create Columns in DataTable

Next, I will create 4 rows with different sets of data:

Page 28: Releavantcodes

Add Data to Columns

Below is the syntax to retrieve data from the DataTable’s local sheet:

'If retrieving data from Local DataTable (Action1, Action2,.. ActionN)DataTable("ColumnName", dtLocalSheet) 'If retrieving data from Global DataTable (dtGlobalSheet)DataTable("ColumnName", dtGlobalSheet)

Therefore, we can now create 2 statements using the syntax above to retrieve our user name and password:

DataTable("UserName", dtLocalSheet)DataTable("Password", dtLocalSheet)

Before we create our script, we must first tell QTP to run the test from all rows in the Local DataTable. In Keyword view, right-click Action1 then click Action Call Properties:

Right-Click Action1

In the Run Tab of Action Call Properties window, select “Run on all rows”. A Dialog will popup mentioning what we discussed in the beginning of this post, about ending our iteration where the first step in our process begins.

Page 29: Releavantcodes

Action Call Properties Window

Close the dialog, then click OK to save the settings we made under Action Call Properties. We are now ready to create our script which will take data from each row and test each UserName/Password combination. Below will be the steps in our process:

1. Launch application2. Enter UserName/Password from DataTable and click Login3. Verify if the Find Flights page appears4. If Find Flights page appears, iteration passed.5. Return to Home Page and start Step #2'Step 1If DataTable.LocalSheet.GetCurrentRow = 1 Then SystemUtil.Run "iexplore.exe", "http://newtours.demoaut.com"End If

After executing the above code, or manually launching the application, execute the below steps:

'Parameterization Block:With Browser("title:=Welcome: Mercury Tours", "index:=0")

'Step 2 'Parameter 1: UserName

.WebEdit("name:=userName").Set Datatable("UserName", dtLocalSheet) 'Parameter 2: Password

.WebEdit("name:=password").Set Datatable("Password", dtLocalSheet)

.Image("name:=login").ClickEnd With 'Step 3'If Find a Flight page appears, go back to HomeIf Browser("title:=Find a Flight: Mercury Tours:", "index:=0").Exist(10) Then

'Step 4Reporter.ReportEvent micPass, "Iteration " &

DataTable.LocalSheet.GetCurrentRow, _"UserName: " & Datatable("UserName", dtLocalSheet) & " is

valid"

Page 30: Releavantcodes

Browser("title:=Find a Flight: Mercury Tours:", "index:=0").Link("text:=Home").ClickElse 'Step 5 'Else, fail the iteration and return to the Home page

Reporter.ReportEvent micFail, "Iteration " & DataTable.LocalSheet.GetCurrentRow, _

"UserName: " & Datatable("UserName", dtLocalSheet) & " is invalid"

Browser("title:=Sign-on: Mercury Tours", "index:=0").Link("text:=Home").ClickEnd If

Once the above script ends its execution, your test results should look like this:

Execution Results

This topic covered a very high-level process of parameterization, but I hope it gives you a good idea how this process can be enhanced and adapted to your application. The next topic in Parameterization will cover Excel Spreadsheets and a few tricks that we can use for successfully driving different sets of data to our AUT.

QTP: Get Link’s Mouse Over (Hover) Color

This post describes a simple technique that can enable retrieval of a Link’s onMouseOver color. The only trick is to change the ReplayType setting to Mouse Events while firing an onMouseOver event on the link. Changing the event cause an actual FireEvent action, that you would encounter when you hover the mouse on the link manually. Below, you will see that the hover event is triggered with the ReplayType set to Mouse Events:

Setting.WebPackage("ReplayType") = 2Browser("Browser").Link("Link").FireEvent "onMouseOver"MsgBox Browser("Browser").Link("Link").Object.currentStyle.colorSetting.WebPackage("ReplayType") = 1

Page 31: Releavantcodes

The above snippet can be simplified and be reused using the function below:

Function GetHoverColor(oLink) Setting.WebPackage("ReplayType") = 2 oLink.FireEvent "onMouseOver" GetHoverColor = oLink.Object.currentStyle.color Setting.WebPackage("ReplayType") = 1End Function RegisterUserFunc "Link", "GetHoverColor", "GetHoverColor"

Now, whenever the link color is to be retrieved, you can simply call the “GetHoverColor” function as you would normally use the Click event:

MsgBox Browser("Browser").Page("Page").Link("Link").GetHoverColor

I hope you find this useful.

QuickTest’s Custom CheckPoints and Beyond This article will discuss the concepts of Custom Checkpoints, their benefits, when to use them and how they provide us with greater flexibility in script development. Custom CheckPoints offer an easier and highly flexible way of constructing your object verifications, which is a core concept of test automation. The concepts below, when coupled together can help you build extremely robust scripts, with enough information that your user desires. Before we begin creating CheckPoints as we would for our application(s), let’s do a quick walk-through of the Reporter Object.

Reporter Object According to QTP help:

Description: The object used for sending information to the test results.

We use the Reporter object along with its ReportEvent method to send the necessary information to the test results:

ReportEvent: Reports an event to the test results.

Below is the syntax of Reporter.ReportEvent method:

Reporter.ReportEvent EventStatus, ReportStepName, Details [, Reporter]

Possible choices for ReportEvent are:

0 or micPass: Causes the status of this step to be passed and sends the specified message to the report.1 or micFail: Causes the status of this step to be failed and sends the specified message to the report. When this step runs, the test fails.2 or micDone: Sends a message to the report without affecting the pass/fail status of the test.3 or micWarning: Sends a warning message to the report, but does not cause the test to stop running, and does not affect the pass/fail status of the test.

Page 32: Releavantcodes

Example:

Reporter.ReportEvent micDone, "Step 1", "Step Done."

I wanted to cover the Reporter object real quick because its a core-concept in creating custom checkpoints. We’ll begin creating them now!

Custom CheckPoints and the Exist MethodThis is an extremely important concept, and in my opinion, it should serve as the base to all CheckPoints. It checks whether an object exists on the page or not, and before we can start verifying an object’s properties, it’s important that we check if it is even available to us to perform our verifications. Also, if the object is found in the applicantion, the Exist method returns true, else, it returns false. Syntax:

Object.Exist(Timeout)

In the syntax above, Timeout is the amount of time you would like QTP to wait for the object before it returns a boolean output. Let’s see 2 examples of its use.

1. Case where an object is found:

To check if the Text box exists, we can formulate the following code that will wait for a max of 5 seconds before the WebEdit appears on the page:

MsgBox Browser("title:=.*Relevant Codes.*").Page("micclass:=Page")_ .WebEdit("name:=txtExistTest").Exist(5)

2. Case where an object is not found. Let’s use the same example above, and run the same check

for the text box below:

MsgBox Browser("title:=.*Relevant Codes.*").Page("micclass:=Page")_ .WebEdit("name:=txtNotFound").Exist(5) 'False

You may spy on the WebEdit above (just to make sure), and notice that there is actually a textbox with the description we provided. But, still, QuickTest returns a failed status. That is because, for this example, I created 2 such text boxes with the exact same description, so if you just provide the property without an Ordinal Identifier, your code will not work. However still, I feel the above is quite a good example of a scenario that can very well happen in many applications and its a good test of an automation engineer to be cautious from the start. However, we still haven’t created a CheckPoint yet. A CheckPoint is a conditional method that information the user of an objects’ status during testing. Thus, we must create a conditional method here:

If Browser("title:=.*Relevant.*").WebEdit("name:=txtNotFound").Exist(5)Reporter.ReportEvent micPass, "WebEdit CheckPoint", "WebEdit was

found. Step Passed."Else

Page 33: Releavantcodes

Reporter.ReportEvent micFail, "WebEdit CheckPoint", "WebEdit was not found. Step failed."End If

There you go! You have just created a custom CheckPoint. If you would like to know more about the Reporter object, please use the QTP help, which is an excellent source of information. Custom CheckPoints and the GetROProperty Method : GetROProperty is short for Get-Runtime-Object-Property. In other words, this method can be used to retrieve the value that the object has, at present. Please note that, in some dynamic applications, runtime object properties can change on various factors. Also, with Descriptive Programming (DP), regardless of what property you retrieve, it will always be a runtime property. Obviously, and I bet you know this, but just to reiterate, all these properties can be viewed from the Object Spy. You can find more information on it in my article Descriptive Programming 1. Below is a snapshot of the TO Properties Tab in Object Spy:

QTP Object Spy TO Properties : You will notice that even though I mentioned RO (RunTime Object) properties, I am looking at the TO (Test Object) tab. This is because, when we use the Object Spy to retrieve an objects’ properties, we are doing it on a runtime object, thus, what we see in the image above are all RunTime object properties with their image in the TO Object properties tab. We will use the GetROProperty method with TO properties. Also, when writing custom checkpoints, we will (almost) always use RO properties instead of TO properties. That is because, we would like to know what the object contains at present, or at runtime as opposed to what the object contained during record time. However, if you are using Descriptive Programming, regardless of what property you retrieve from your application, it will be a runtime property. The textbox below should have a value of 10, for the maxLength property. We

will create a checkPoint to test that assumption.

Page 34: Releavantcodes

If Browser("title:=.*Relevant.*").WebEdit("name:=txtMaxLen").GetROProperty("max length")=10 Then

Reporter.ReportEvent micPass, "WebEdit MaxLength Text", "Test Passed."Else

Reporter.ReportEvent micFail, "WebEdit MaxLength Text", "Test Failed."End If

Result:

QTP Results :

QTP Results : When you open your Test Results after the above statement executes, you will notice that, the test failed. It will always fail because the maxLength for our WebEdit is not 10, but 9. When you you use Object spy to retrieve the max length property, you will notice that it is indeed not 10, but its 9. However, in the real-world, we may not always know the properties these objects will have at runtime, and most of the times you will come to find of errors after your regression suites complete their execution.

Let’s create another CheckPoint, and verify if the Link has the correct color:

Relevant Codes: Test Color

With Browser("title:=.*Relevant Codes.*").Link("text:=Relevant Codes: Test Color")

If .Exist(5) ThenIf .GetROProperty("color") = "#0000ff" Then

Reporter.ReportEvent micPass, "RelevantCodes Link", "Correct color."

ElseReporter.ReportEvent micFail, "RelevantCodes Link",

"Incorrect color."End If

ElseReporter.ReportEvent micFail, "RelevantCodes Link", "Link was

not found."End If

End With

Result:

QTP Results

Page 35: Releavantcodes

QTP Results

CheckPoints for Multiple Objects : There are circumstances where you need to create CheckPoints for multiple objects at once. For instance, if you are creating a Login Function, you may need to create a CheckPoint for the UserName Textbox, Password Textbox and the Submit button. This section of the article details how CheckPoints can be created for multiple objects. This article shows a way to verify multiple Object Properties through a single Class at once, and reports all Checks in a tabular form to QuickTest Results. Below, you will see a scenario that I just mentioned above. These objects won’t do anything, but, they show a way you can create CheckPoints to be run for multiple objects together.

With Browser("title:=.*Relevant Codes.*").Page("micclass:=Page") If .WebEdit("name:=txtUser").Exist(1) And .WebEdit("name:=txtPassword").Exist(1) Then Reporter.ReportEvent micPass, "UserName/Password", "Objects found." Else Reporter.ReportEvent micFail, "UserName/Password", "Objects not found." End IfEnd With

Multiple Checks - Results

Multiple Checks - Results

Similarly, if we want to verify objects’ properties instead of checking whether they exist, we can do this:

With Browser("title:=.*Relevant Codes.*").Page("micclass:=Page") If .WebEdit("name:=txtUser").GetROProperty("max length") = 9 And _ .WebEdit("name:=txtPassword").GetROProperty("max length") = 9 Then

UserName Passw ord

Page 36: Releavantcodes

Reporter.ReportEvent micPass, "UserName/Password", "Correct MaxLength" Else Reporter.ReportEvent micFail, "UserName/Password", "Incorrect MaxLength" End IfEnd With

QTP Results

QTP Results

CheckPoints for Hidden ObjectsLet’s create another CheckPoint. This time, things will get a little complex. Below you will see a Textbox, but indeed there are 2. One of them is hidden. Is there a way to know that one of these

Text boxes is hidden? The answer is: Yes.

With Browser("title:=.*Relevant Codes.*")'If the first text box, that is, the text box with index 0 is hidden then.. set value in Text Box #2 If .WebEdit("name:=txtIsVisible", "index:=0").GetROProperty("visible") = False Then Browser("title:=.*Relevant Codes.*").WebEdit("name:=txtIsVisible", "index:=1").Set "2nd" Else 'Else, set value in Text Box #1 Browser("title:=.*Relevant Codes.*").WebEdit("name:=txtIsVisible", "index:=0").Set "1st" End IfEnd With

In practice, many people make the mistake of omitting an ordinal identifier in such cases. When you run the same code without an ordinal identifier:

'ErrorMsgBox Browser("title:=.*Relevant Codes.*").WebEdit("name:=txtIsVisible").GetROProperty("visible")

Page 37: Releavantcodes

You can generally run the following bit of code to find out which object is hidden:

Set oDesc = Description.CreateoDesc("name").value = "txtIsVisible" Set colObject = Browser("title:=.*Relevant Codes.*").Page("micclass:=Page").ChildObjects(oDesc) For x = 0 to colObject.Count - 1

Print "Object " & x & ": " & colObject(x).GetROProperty("visible")Next

In your print log, you will have the following information:

'Object with Index = 0 is HiddenObject 0: False

'Object with Index = 1 is not HiddenObject 1: True

There are more ways to hide objects, and there are several techniques that can be used to spot them. However, I will let my readers explore those scenarios and find the many possibilities available to them. I hope this article clarifies some doubts, and gives you more confidence using them in your everyday development.

Recovery Scenario Test Run Error: Item in list or menu not found Automated tests are created mainly to be run unattended, and this raises a few concerns for automation developers. Assume a situation where you initiated your test-suite while leaving your office to be run over-night. However, because of unforeseen errors, your test stops at a point and disrupts your results. When you return to your desk the next day, only to find that your tests aren’t completely executed. This can be quite frustrating, and its crucial to be proactive and handle such errors before they impact your work. This article discusses a way of creating a Recovery Scenario for an instance where an item in the list item or menu is not found. In this example, we will define a Function Call and use that Function Call to handle the error. The default syntax for the Recovery Scenario Function is:

Function fnRecovery(Object, Method, Arguments, retVal) 'Error Handling CodeEnd Function

Explanation of each argument to fnRecovery is given below:

Page 38: Releavantcodes

Object as Object: The object of the current step.Method as String: The method of the current step.Arguments as Array: The actual method's arguments.Result as Integer: The actual method's result.

To handle this scenario, we will use the function below:

Function Recovery_ListItemIsNotFound(Object, Method, Arguments, retVal) Dim sAllItems, arrAllItems, intItem  With Object 'Retrieve all items from the Listbox sAllItems = .GetROProperty("all items") 'Split 'all items' using a delimiter ";" into an array arrAllItems = Split(sAllItems, ";")  'Select a random number intItem = RandomNumber.Value(LBound(arrAllItems), UBound(arrAllItems)) .Select "#" & intItem  Reporter.ReportEvent micInfo, "ListItemIsNotFound", "Item: " & .GetROProperty("value") End WithEnd Function

Recovery_ListItemIsNotFound, as the same suggests, executes the Recovery Operation if the list item that we supplied to our target WebList does not exist. This is quite common in Web applications, and Items in a WebList can change depending upon the input(s) provided.

To start, Click Resources -> Recovery Scenario Manager. You should see a window like this:

Recovery Scenario Wizard

In the window, click the following button:

Doing so will invoke the Recovery Scenario Wizard:

Page 39: Releavantcodes

Recovery Scenario Wizard – Welcome , When the window above opens:

1. Click Next 2. Select Test Run Error as the Trigger Event

3. Click Next 4. Select ‘Test Run Error: Item in list or menu not found in the Error Listbox

5. Click Next twice and navigate to Recovery Operation

Page 40: Releavantcodes

6. Select Function Call and Click Next

7. Select a library which will store our Recovery Function.

8. Select ‘Define New Function’ and in the TextArea, paste the function Recovery_ListItemIsNotFound

Define New Function

9. Click Next 10. Make sure ‘Add Another Recovery Scenario’ Checkbox is not selected and click Next

again.

Page 41: Releavantcodes

11. Under Post-Recovery Test Run Options, select proceed to next step.

12. Give your scenario a name and Click Next

13. And finally, Check the following option: Add Scenario to current test and Click Finish. Save Changes before closing.

We will use the WebList below to select a value that does not exist in it; so, let’s write a code to select Rational Robot. If you view the items present in the listbox, you will notice that Rational Robot indeed doesn’t exist, but our Recovery Scenario will handle the error when we select it.

Instead, it will select a random value from the list.

Browser("title:=.*Recovery Scenario.*").Page("micclass:=Page")_ .WebList("name:=testTools").Select "Rational Robot" MsgBox "Item Selected: " & Browser("title:=.*Recovery Scenario.*").Page("micclass:=Page")_

QuickTest Pro

Page 42: Releavantcodes

.WebList("name:=testTools").GetROProperty("value")

When the code above executes, you will notice that, instead of throwing a Test Run Error, a random value from the list box was selected. You will know that the Recovery Scenario was triggered and ran successfully when you view the Test Results:

Recovery Scenario Executed

There are plenty of other scenarios as well, and I will try to cover some of the important ones. If you have any suggestions, or a scenario that you would like me to cover first, please use the comments section of this post to share your ideas/thoughts with me. Thanks! :)

QTP: Working with Multiple Browser Applications (Revised) Introduction : Web applications due to their dynamic nature, make script development quite challenging. I am writing this article in an attempt to simplify one of its aspects that automation developers face while working with Web applications. We will see how it can be made extremely easy to work with multiple browsers with the use of a Dictionary object, to which we can add Browsers, remove them, change the way we reference them and much more. You will notice in the examples at the end of this article that, regardless of how many pages we navigate, we would never have to keep a track of changing titles (unless we need to). In other words, regardless of browser navigation and actions performed on each browser, this concept will help use the name we give them to work with them, instead of following their ever-changing properties. Let’s begin by creating a global variable, that will hold the Browser Collection to be accessed by our class:

'Public Variable: Holds Browser CollectionPublic colBrowser

Another reason to create a global variable is that as long as it is an object, it can be used as a reference by (local) class variables, thus being over-written as many number of times as we want. Next, we will create a Browser class, that will make use of the global variable (as a reference) we declared above:

Class clsBrowser End Class

To ensure that we are not creating a new object each time our class initiates, we must create a Singleton, which will be stored in the initialization procedure of our class. It will also assure us

Page 43: Releavantcodes

that our code is highly efficient and our global variable is created only once, and not destroyed unless required.

' Purpose: Initializes the Scripting.Dictionary SingletonPrivate Sub Class_Initialize

Dim bInit: bInit = False 

' If colBrowser has already been instantiated, then Init = TrueIf IsObject(colBrowser) Then

If Not colBrowser Is Nothing ThenbInit = True

End IfEnd If

 ' If colBrowser was destroyed or has not yet instantiated, then create

itIf bInit = False Then Set colBrowser =

CreateObject("Scripting.Dictionary") 

' colObject (local) acts as a reference to colBrowsercolObject = colBrowser

End Sub

Above, colObject is a reference to our Global Collection object colBrowser.

Instead of creating a new object each time, we will reuse the same reference to add all the necessary Browsers. To add browsers to our collection, let’s create a simple method called “AddBrowser” and a public property “HWND”, that will store the Windows Handle of the Browser:

' Purpose: Adds Browsers and their HWNDs to a CollectionSub AddBrowser(sName)

' If the Name already exists in the collection, then remove it so it can be re-added

If colObject.Exists(sName) ThencolObject.Remove sNamecolObject.Remove sName & "-HWND"

End If 

' Add the Browser with its corresponding handle' Store the Handle PropertyWith colObject

.Add sName, Browser("hwnd:=" & Me.HWND)

.Add sName & "-HWND", Me.HWNDEnd With

End Sub Private Handle ' Purpose: Stores the Browser HandlePublic Property Let HWND(ByVal Val)

Handle = val End PropertyPublic Property Get HWND()

Page 44: Releavantcodes

HWND = Handle End Property

AddUsingCreationTimeTo provide ourselves with more options to add browsers, let’s create 3 more methods: AddUsingCreationTime, AddUsingTitle and AddLastOpen. As the name suggests, AddUsingCreationTime will enable us to add the browser in our collection object using its creationtime:

' Purpose: Uses the "AddBrowser" method to add browsers to the collection ' using their CreationTime PropertyPublic Sub AddUsingCreationTime(sName, intCreationTime)

Dim oBrowser, oCol 

' Description object for Browser ClassSet oBrowser = Description.CreateoBrowser("micclass").Value = "Browser"

 ' ChildObjects of Browser Class DescriptionSet oCol = Desktop.ChildObjects(oBrowser)

 'If the supplied CreationTime is greater than the total number of open

browsers, 'then Report Err.

If intVal > oCol.Count ThenReporter.ReportEvent micWarning, "Add Browser Using

CreationTime", "Browser " & _ "with CreationTime " &intCreationTime& " was not found."

Exit SubEnd If

 ' Store the Browser HandleMe.HWND = Browser("creationtime:=" &

intCreationTime).GetROProperty("HWND") 

' Add the browser to the collectionAddBrowser sName

End Sub

AddUsingTitleSimilarly, AddUsingTitle will enable us to store a Browser if we prefer using Browser’s Title:

' Purpose: Uses the "AddBrowser" method to add browsers to the collection ' using their Title PropertyPublic Sub AddUsingTitle(sName, sTitle)

' Verify if the browser with the supplied title existsIf Not Browser("title:=" & sTitle).Exist(1) Then

Reporter.ReportEvent micWarning, "Add Browser Using Title", "Browser " & _ "with Title " &sTitle& " was not found."

Exit SubEnd If

 ' Store the Browser Handle

Page 45: Releavantcodes

Me.HWND = Browser("title:=" & sTitle).GetROProperty("HWND") 

' Add the browser to the collectionAddBrowser sName

End Sub

AddLastOpenLastly, for greater flexibility, we will create another method, AddLastOpen, which as the name suggests, will add only the most current browser to our collection:

' Purpose: Uses the "AddBrowser" method to add the last (most recent) open browser' Note: The last open browser always has the greatest CreationTimePublic Sub AddLastOpen(sName)

Dim oBrowser, oCol 

' Description object for Browser ClassSet oBrowser = Description.CreateoBrowser("micclass").Value = "Browser"

 ' ChildObjects of Browser Class DescriptionSet oCol = Desktop.ChildObjects(oBrowser)

 ' Store the Browser HandleMe.HWND = Browser("creationtime:=" & oCol.Count -

1).GetROProperty("HWND") 

' Add the browser to the collectionAddBrowser sName

End Sub

To simplify calling of objects, we will use the names we give to each browser. Instead of using .item, we can use .Name which is more descriptive. This part can be omitted, but for the sake of completion, let’s create this method anyways:

Public Function Name(Key)Dim Keys

 Keys = colObject.Keys

 If IsNumeric(Key) Then

Key = Keys(Key)End If

 If IsObject(colObject.Item(Key)) Then

Set Name = colObject.Item(Key)Else

Name = colObject.Item(Key)End If

End Function

Finally, we must create an instance of the object, that will enable us to call class methods:

' Create a new instance of Class clsBrowser

Page 46: Releavantcodes

Set BrowserObject = New clsBrowser

We’re done! You can download the class here, or view the text version here

Demonstration: AddUsingCreationTimeAs a demonstration, you can associate (or ExecuteFile) the library and run the following lines of code:

SystemUtil.Run "iexplore.exe", "http://newtours.demoaut.com", "", "", 3 : Wait(4)'Add the First open browser (creationtime=0) to the collectionBrowserObject.AddUsingCreationTime "DemoAUT", 0 SystemUtil.Run "iexplore.exe", "http://relevantcodes.com", "", "", 3 : Wait(4)'Add the Second open browser (creationtime=1) to the collectionBrowserObject.AddUsingCreationTime "RelevantCodes", 1 'Use the names we gave the browser, and use the same name regardless of changes in its propertiesWith BrowserObject.Name("DemoAUT")

.WebEdit("name:=userName").Set "test"

.WebEdit("name:=password").Set "test"

.Image("name:=login").Click 

.Sync 

If .WebList("name:=fromPort").Exist(10) Then.WebList("name:=fromPort").Select "Frankfurt".WebList("name:=fromMonth").Select "December".WebList("name:=toPort").Select "Paris".WebList("name:=toMonth").Select "December".WebRadioGroup("name:=servClass").Select "#1".WebList("name:=airline").Select "Unified Airlines".Image("name:=findFlights").Click

End IfEnd with 'Use the names we gave the browser, and use the same name regardless of changes in its propertiesWith BrowserObject.Name("RelevantCodes")

.Link("text:=Articles", "index:=0").Click

.Link("text:=Home", "index:=0").Click

.Link("text:=QTP\/Web", "index:=0").ClickEnd with With BrowserObject

.Name("DemoAUT").Close

.Name("RelevantCodes").CloseEnd with 'ReleaseBrowserObject.Destroy

Page 47: Releavantcodes

Demonstration: AddUsingTitleAs stated earlier, this method will store any browser with the provided title.

SystemUtil.Run "iexplore.exe", "http://newtours.demoaut.com", "", "", 3 : Wait(4)'Add the above open browser using its titleBrowserObject.AddUsingTitle "DemoAUT", ".*Mercury Tours.*" SystemUtil.Run "iexplore.exe", "http://relevantcodes.com", "", "", 3 : Wait(4)'Add the above open browser using its titleBrowserObject.AddUsingTitle "RelevantCodes", ".*Relevant Codes.*" 'Use the names we gave the browser, and use the same name regardless of changes in its propertiesWith BrowserObject.Name("DemoAUT")

.WebEdit("name:=userName").Set "test"

.WebEdit("name:=password").Set "test"

.Image("name:=login").Click 

.Sync 

If .WebList("name:=fromPort").Exist(10) Then.WebList("name:=fromPort").Select "Frankfurt".WebList("name:=fromMonth").Select "December".WebList("name:=toPort").Select "Paris".WebList("name:=toMonth").Select "December".WebRadioGroup("name:=servClass").Select "#1".WebList("name:=airline").Select "Unified Airlines".Image("name:=findFlights").Click

End IfEnd with 'Use the names we gave the browser, and use the same name regardless of changes in its propertiesWith BrowserObject.Name("RelevantCodes")

.Link("text:=Articles", "index:=0").Click

.Link("text:=Home", "index:=0").Click

.Link("text:=QTP\/Web", "index:=0").ClickEnd with With BrowserObject

.Name("DemoAUT").Close

.Name("RelevantCodes").CloseEnd with 'ReleaseBrowserObject.Destroy

Demonstration: AddLastOpenAs stated earlier, this method will add the last open browser to the global browser collection.

SystemUtil.Run "iexplore.exe", "http://newtours.demoaut.com", "", "", 3 : Wait(4)'Add the last open browser to the collectionBrowserObject.AddLastOpen "DemoAUT"

Page 48: Releavantcodes

 SystemUtil.Run "iexplore.exe", "http://relevantcodes.com", "", "", 3 : Wait(4)'Add the last open browser to the collectionBrowserObject.AddLastOpen "RelevantCodes" 'Use the names we gave the browser, and use the same name regardless of changes in its propertiesWith BrowserObject.Name("DemoAUT")

.WebEdit("name:=userName").Set "test"

.WebEdit("name:=password").Set "test"

.Image("name:=login").Click 

.Sync 

If .WebList("name:=fromPort").Exist(10) Then.WebList("name:=fromPort").Select "Frankfurt".WebList("name:=fromMonth").Select "December".WebList("name:=toPort").Select "Paris".WebList("name:=toMonth").Select "December".WebRadioGroup("name:=servClass").Select "#1".WebList("name:=airline").Select "Unified Airlines".Image("name:=findFlights").Click

End IfEnd with 'Use the names we gave the browser, and use the same name regardless of changes in its propertiesWith BrowserObject.Name("RelevantCodes")

.Link("text:=Articles", "index:=0").Click

.Link("text:=Home", "index:=0").Click

.Link("text:=QTP\/Web", "index:=0").ClickEnd with With BrowserObject

.Name("DemoAUT").Close

.Name("RelevantCodes").CloseEnd with 'ReleaseBrowserObject.Destroy

Notice the demos above. We only have to use the custom name we gave to the browser to perform events on the objects that exist inside of it. Our custom names can be used throughout the automation cycle, but I would recommend you to look into the “ChangeName” method available in the class. This would make object naming easier, and more descriptive, as the titles change the moment we navigate to another page.

QTP: Creating a Highly Efficient Test-Data Dictionary This article discusses a simple way to use test data with the help of the Dictionary object. I know many of you store data in Excel, and some of you may read the entire spreadsheet as a range, or retrieve data through individual cells. Some of you also use Access, SQL Server, Text files etc. This concept is not really about storing test data effectively, but it will discuss how we can use that data in a way that is easily understood without referencing our data sources over and over again. Instead, you

Page 49: Releavantcodes

access your data source only once. Also, this article shows a highly efficient and fast performing approach of creating our test data dictionary. Let’s first start with creating our Scripting.Dictionary, that will store the entire test data. In other words, our dictionary object will contain everything that we would otherwise retrieve from our data pool.

Set oDataDict = CreateObject("Scripting.Dictionary")

Above, we created the dictionary object that is going to store our test data descriptively. Let’s now add a few entities to our dictionary manually:

With oDataDict.Add "UserName", "test".Add "Password", "test".Add "TripType", "roundtrip".Add "PassCount", "2".Add "FromPort", "Frankfurt".Add "FromMonth", "December".Add "FromDay", "9".Add "ToPort", "Paris".Add "ToMonth", "December".Add "ToDay", "27".Add "ServClass", "Coach".Add "Airline", "Unified Airlines"

End With

All the entities in the dictionary object above are for HP/Mercury Demo Flight Application.

Now, we can simply use our data dictionary to write values to our application:

SystemUtil.Run "iexplore.exe", "http://newtours.demoaut.com", "", "", 3 With Browser("title:=Welcome: Mercury Tours").Page("micclass:=Page")

If .Exist(5) Then.WebEdit("name:=userName").Set oDataDict("UserName").WebEdit("name:=password").Set oDataDict("Password").Image("name:=login").Click

End IfEnd With Set oDataDict = Nothing

This part was quite simple, but, there is more, a lot more. Now, how would we build this dictionary object if all our data was in Excel? What I would do in such a situation is read the entire spreadsheet in a range, and use the range-array to write values to my object. Even though the columns change, I would only need to rebuild my dictionary, and never mess with my action libraries. However, there is an even better approach, and that is with the use of ADO and it is explained in detail below. With Excel, we can use the Class clsTestData below to build our dictionary with great performance and have the exact same dictionary as we created manually (but without much effort):

'===============================================================

Page 50: Releavantcodes

' Name: Class clsTestData'' Purpose: Creates a TestData Dictionary'' Functions:' BuildContext (Private)' Load (Public)'' Properties:' oDict'' Author: Anshoo Arora'' Version: 0.1'===============================================================Class clsTestData Private mDict 'Local Instance of Scripting.Dictionary

Public sWorkBook 'Excel WorkBookPublic vSheet 'Excel WorkSheetPublic iRow 'Excel Row where test data is contained

'=======================================================' Name: Function Load'' Purpose: This function makes the TestData dictionary available' to the test.'' Input:' sWorkBook: Path to the Workbook where test data is stored' vSheet : Name of the Worksheet where the data is stored' iRow : Row where the data is retrieved from'' Output:' Object- Scripting.Dictionary'=======================================================Public Default Function Load(sWorkBook, vSheet, iRow)

With Me.sWorkBook = sWorkBook.vSheet = vSheet.iRow = iRow

End With 

BuildContext : Set Load = oDictEnd Function

 '=======================================================' Name: Function BuildContext'' Purpose: This function does the core operation of building the' test data dictionary.'' Input:' None'' Output:' None'=======================================================

Page 51: Releavantcodes

Private Function BuildContextDim oConn, oRS, arrData, xCONST adOpenStatic = 3CONST adLockOptimistic = 3CONST adCmdText = "&H0001"

 Set oConn = CreateObject("ADODB.Connection")Set oRS = CreateObject("ADODB.RecordSet")

 'Open ConnectionoConn.Open "Provider=Microsoft.Jet.OLEDB.4.0;" &_

"Data Source=" & Me.sWorkBook & ";" & _"Extended Properties=""Excel

8.0;HDR=Yes;"";" 

'QuerysQuery = "Select * From [" & Me.vSheet & "$]"

 'Run query against WorkBookoRS.Open sQuery, oConn, 3, 3, 1

 'Move RecordSet to the target RowFor x = 2 to iRow - 1 : oRS.MoveNext : Next

 oDict = CreateObject("Scripting.Dictionary")

 'Use a For..Loop to Build Scripting.DictionaryFor x = 0 to oRS.Fields.Count - 1

With oDict.Add "" & oRS(x).Name, "" & oRS.Fields(x)

End WithNext

End Function 

Private Property Let oDict(ByVal Val)Set mDict = Val

End PropertyPrivate Property Get oDict()

Set oDict = mDictEnd Property

End ClassSet mDataContext = New clsTestData

Download the Class: clsTestData

Since our class is ready, we can use it to retrieve data and build our dictionary from the table below:

Page 52: Releavantcodes

Excel Table with Test Data

You might think that this will have a drastic impact on performance, and that your script will become slow. Actually, I have a table with 72 columns and it took me 0.3125 seconds to load the entire row to the dictionary.

We can use the below code to build our test data dictionary at runtime using the Table above:

sWorkBook = "C:\Test.xls" 'WorkBookvSheet = "Sheet1" 'WorkSheetiRow = 2 'Row Number

Set mDict = mDataContext.Load(sWorkBook, vSheet, iRow) 'Took 0.156 seconds to build

MsgBox mDict("Execute_Test") 'YMsgBox mDict("Scenario Name") 'Scenario 1MsgBox mDict("Username") 'testMsgBox mDict("Password") 'testMsgBox mDict("Trip Type") 'roundtripMsgBox mDict("Passenger Count") '1MsgBox mDict("From Port") 'FrankfurtMsgBox mDict("From Month") 'AprilMsgBox mDict("From Day") '10

Note: All names used in the Dictionary Object correspond to the column names in the Excel spreadsheet. Execute_Test, Scenario Name, Username etc. are exactly as shown in the heading of our Table above.

The Question: How and where should I use this? Its recommended to load all test data at the very beginning of the test. The only difference here is, one single variable stores all your data in a descriptive way, so it becomes very easy to know what data is being input to our application’s fields. If you have a driver script and a class member that loads all the global variables and objects, then that would be an ideal place to load this dictionary. In my driver script, this is how I load my globals:

'DataDict will contain all test data'Its public status dictates that it can be accessed by other classes and functionsPublic DataDict Class clsDriver  Public Default Function Run LoadGlobals  'Driver Code

UnloadGlobals End Function  Private Function LoadGlobals

Page 53: Releavantcodes

Set DataDict = mDataContext.Load(sWorkBook, vSheet, iRow) End Function  Private Function UnloadGlobals Set DataDict = Nothing End Function End Class

I hope this article will help you create a highly efficient way of dealing with your test data. Thanks for reading. :)

Custom View Options for QTP’s Expert-View Editor Today, after being quite tired of switching windows between Notepad++ and QTP, I started experimenting with different layouts I can create to make the the QuickTest editor a little more viewing-friendly. After a few minutes of playing around, I finally have something that I like. I wanted to share what I have with you guys, and I hope if you have custom layouts for QTP’s editor, please feel free to share them.

My Custom Settings for QTP's Editor - View Options

The Layout elements can be accessed from the following menu:

Tools -> View Options -> Fonts and Colors

Values for each element to create the layout in the snapshot:

Font name: Courier NewSize: 9Elements:

Comment: (Foreground: Green) (Background: White) (Style: Normal)Default: (Foreground: Black) (Background: White) (Style: Normal)Left Margin: (Foreground: Black) (Background: Silver) (Style: Normal)Number: (Foreground: Red) (Background: White) (Style: Bold)Operator: (Foreground: Black) (Background: White) (Style: Bold)Reserved Word: (Foreground: Navy) (Background: White) (Style: Bold)Selected Text: (Foreground: White) (Background: Navy) (Style: Normal)

Page 54: Releavantcodes

String: (Foreground: Maroon) (Background: White) (Style: Normal)

I should have done this a long time ago :)

QTP: Creating Object Descriptions “On The Fly” This article is a follow-up to the Descriptive Programming Series and will discuss a simple way of creating Object Descriptions on the fly. There are several reasons for using this approach, such as overcoming the need to write extra code and having the ability to make the change in only a single place while performing maintenance. This article includes the concepts and working examples of how and when we can must use this approach. There are instances where object descriptions are incremented in a numerical context. In other words, a part of the object description stays the same, but these descriptions increment starting at a number, generally 0.

To illustrate, consider the below HTML_IDs for 4 list boxes in a Web Application:

List Box 1 HTML ID -> lstBox0List Box 1 HTML ID -> lstBox1List Box 1 HTML ID -> lstBox2List Box 1 HTML ID -> lstBox3

Notice how the descriptions above increment. lstBox is common in all 4 descriptions, but there is a numerical increment after each, starting at 0. When creating an object description in such a scenario, you may tend to do the following:

Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstBox0").Select "Item"Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstBox1").Select "Item"Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstBox2").Select "Item"Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstBox3").Select "Item"

The above is a common usage, and completely acceptable. But in practice, we may have more than 4 Objects with such descriptions and creating the same line of code over and over again, and maintaining everything can be quite a task. In such situations, the concept in this article can be quite beneficial. For example, the above code snippet can be condensed into:

'use a variable to increment from 0 to 3For x = 0 to 3

Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstBox" & x).Select "Item"Next

Now, if the HTML_ID of this object changes, we have to make the change only once as opposed to several times. For example, if the HTML_ID of the list box above was to change from lstBox to lstObject, we would simply update our code like this:

For x = 1 to 4'lstBox becomes lstObject

Page 55: Releavantcodes

Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstObject" & x).Select "Item"Next

Instead of updating it 4 times:

Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstObject0").Select "Item"Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstObject1").Select "Item"Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstObject2").Select "Item"Browser("SomeBrowser").Page("SomePage").WebList("html id:=lstObject3").Select "Item"

Numeric IncrementsLet’s see a working example. Please open a new IE browser and navigate to the following Page: Demo 1. You can simply set values in all of the text boxes using the following snippet:

With Browser("name:=SimpleDemo1.*").Page("micclass:=Page")For x = 1 to 6

.WebEdit("html id:=txtDemo" & x ).Set "Text Box " & xNext

End With

Alphabetic IncrementsNow imagine a similar situation where numeric increments are replaced with alphabets. There are several ways of working with such scenarios, but what I generally do is create an array with all the alphabets and increment elements of that array to work with runtime objects:

Dim arrAlphabets: ReDim arrAlphabets(25) arrAlphabets = Array("a", "b", "c", "d", "e", "f", "g", "h", _

"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", _"s", "t", "u", "v", "w", "x", "y", "z")

Now, we can use the array of alphabets using the exact same approach we used for numeric descriptions on Demo Page 2.

With Browser("name:=SimpleDemo2.*").Page("micclass:=Page") For x = 1 to 6 .WebEdit("html id:=txtDemo_" & arrAlphabets(x-1)).Set "Text Box " & UCase(arrAlphabets(x-1)) NextEnd With

If you are starting off with Descriptive Programming and VBScript Arrays, and would like to learn how the above approach works, please ask your questions in the comments section and I will try my best to help you.

Page 56: Releavantcodes

Complex IncrementsThe above 2 uses of this technique are quite basic, and chances are that in your environment, you may not see such simple instances. Let’s create another scenario then, with a little complex description. Please open a new browser, and navigate to Demo Page 3.

When you spy on the WebEdits on this page, you will see a long description for the ‘name’ property. The name property of the six WebEdits are:

txt_applicant[0]BuyProductBox_answerString_Atxt_applicant[1]BuyProductItem_answerString_Btxt_applicant[2]BuyProductBall_answerString_Ctxt_applicant[3]BuyProductBat_answerString_Dtxt_applicant[4]BuyProductSpoon_answerString_Etxt_applicant[5]BuyProductFork_answerString_F

How can the description for this object be created such that, we can get our work done through a single line of code, instead of creating 6 different statements to set a value in each text box? Notice the descriptions closely, not only are the applicant numbers incrementing in numerical and alphabetical context, but the product lines are changing as well (Box, Item, Ball etc).

The trick here is to use RegularExpressions/WildCards to make the description generic so it becomes available to all 6 WebEdits. There are several ways this generic description can be created, and if you solved this using a different description, please feel free to share it.

Below is one possible solution for our complex scenario:

Dim arrAlphabets: ReDim arrAlphabets(25)Dim sName arrAlphabets = Array("a", "b", "c", "d", "e", "f", "g", "h", _

"i", "j", "k", "l", "m", "n", "o", "p", "q", "r", _"s", "t", "u", "v", "w", "x", "y", "z")

 With Browser("name:=SimpleDemo3.*").Page("micclass:=Page") For x = 0 to 5 sName = "txt_applicant\[" & x & "\].*_" & arrAlphabets(x) .WebEdit("name:=" & sName).Set "Text Box " & UCase(arrAlphabets(x)) NextEnd With

Summary : This article summarized how working with objects with similar descriptions can be achieved in a simple manner. This approach may not be feasible for all environments, and should only be used when possible. You’re most likely to see Approach#3 in this article used most often, and at times, it does get a little tricky to write descriptions to work with all objects having similar properties. This article is the last in the Introductory DP series, and future articles will cover advanced concepts and techniques of working with real-life applications. I hope you guys find this article useful. Thanks for reading :)

QTP: Understanding the Description Object (Description.Create) Introduction : In this article, we will cover the basics of Description Object and the ChildObjects method. This

Page 57: Releavantcodes

is a very powerful approach to creating robust automation test suites. It is also a very important principle of Descriptive Programming in QTP.

We will see how we can create an object description with a single property, with multiple properties, by using regular expressions and finally putting all these concepts into their application with the ChildObjects method.

Before we see how it is done, we must first understand the following statement:

Set oDesc = Description.Create

Above, we are using the variable oDesc to create a description of something. Since, oDesc is preceeded by a Set statement, it must be an object reference. Therefore, when the above statement executes, oDesc will become an object reference to a description object. We will use the description object with the ChildObjects method to retrieve object collections. These object collections are nothing but arrays of objects with the exact properties of the description object.

If you are starting with DP, please read the article on Descriptive Programming Concepts on Relevant Codes for a quick walk-through on Descriptive Programming before continuing with this article. I. Creating a Description Object for a Parent (Browser/Window)To create an object description for a parent, we will simply use the “Class” of the parent object and add the property-value combination:

Dim oDesc 'Description ObjectDim colObject 'Object CollectionSet oDesc = Description.Create'Remember to always use 'micclass' and not 'class name'oDesc( "micclass" ).value = "Browser" 'We used Desktop as the parent here because, the Desktop Object holds all the WindowsSet colObject = Desktop.ChildObjects( oDesc )

When the above snippet execute, colObject will contain a collection of all browsers visible on the desktop. But, since we have done this, how will we know how many browser objects exist on the desktop through code? Also, how will we retrieve their information? The answer is quite simple: colObject contains the necessary information and methods we can use to perform events on our target objects.

'Retrieve # of open browsersMsgBox colObject.Count 'Retrieve Titles of all open browsersFor x = 0 to colObject.Count - 1

MsgBox colObject(x).GetROProperty("title")Next

As I mentioned above, colObject is nothing but an array of all objects having the exact same properties as the description object. Therefore, colObject(0) points to the first object in the collection, colObject(1) points to the second object in the collection, and so on. This is demonstrated in the For..Next loop in the snippet above.

Page 58: Releavantcodes

II. Creating a Description Object for an Object contained in a Browser/WindowUsing the same approach above, we will retrieve collection of all Link objects on a page. Notice here that instead of Desktop, we have used the entire Browser-Page hierarchy. This is because, a link object is present somewhere inside the Page, instead of the desktop :).

Dim oDesc 'Description ObjectDim colObject 'Object CollectionSet oDesc = Description.CreateoDesc( "micclass" ).value = "Link" 'Using the entire Browser-Page hierarchySet colObject = Browser( "title:=Google").Page("title:=Google").ChildObjects( oDesc )

By executing the snippet above, we will use colObject to perform some events on these objects. Let’s retrieve the number of link objects present in our browser:

'Returns 30MsgBox colObject.Count

The above example is created for the Google page, and if you want to test it for some other browser, do not forget to change the Browser and Page titles to the correct values.

III. Creating a Description Object for an Object contained in a Browser/Window Using Multiple Properties :If you saw the above example, we created a description object using only the class of the Link object. Now, we will retrieve a more specific object collection. We are going to specify an additional property and narrow down the search results- this is because, even though there can be multiple Link objects on a page, its not necessary that there would be the same number of objects with the text “Hello, I am a Link!”. This approach helps us retrieve only the objects that we really want for our automation, since having a collection of all objects can be quite cumbersome to work with. Also, if we provide relevant information to our Description Object, QTP will return a narrowed-down collection, which is the easiest to work with. This is similar to how you search Google. If you specify “QTP” in Google Search, you will have a huge array of search results. You can narrow down the search results (and find information faster) if you add “QTP + Browser + DP” instead.

Dim oDesc 'Description ObjectDim colObject 'Object CollectionSet oDesc = Description.CreateoDesc( "micclass" ).value = "Link"'Additional property- for more focused and controlled collectionoDesc( "text" ).value = "Images" Set colObject = Browser( "title:=Google").Page("title:=Google").ChildObjects( oDesc ) Msgbox colObject.Count 'Returns 1

Now, colObject will only contain the Link objects that have the text: Images. Also, if you notice Google page, there is only one link with the Images text. This is a good example of finding the target object dynamically. Suppose, if there were 2 links on the page instead, we would have both of them in our collection colObject. Let’s highlight our found object:

Page 59: Releavantcodes

colObject(0).Highlight

Executing the statement above, we will highlight the first object in the collection. I know there is only one object in the collection, but we can make sure by doing this:

For x = 0 to colObject.Count - 1colObject(x).Highlight

Next

IV. Creating a Description Object using WildCardsIf we have dynamic objects in our application, then we may not always have the luxury to feed their properties as we have done in the example above. Sometimes, we would need to create dynamic descriptions and retrieve our target objects from resulting (dynamic) collections. I say dynamic collections because at one time, your collection may hold 9 objects, whereas during the next session, it may hold 10.

Dim oDesc 'Description ObjectDim colObject 'Object CollectionSet oDesc = Description.CreateoDesc( "micclass" ).value = "Link"oDesc( "text" ).value = "I.*age.*" 'Images'.regularExpression is 'True' by defaultoDesc( "text" ).regularExpression = True Set colObject = Browser( "title:=Google").Page("title:=Google").ChildObjects( oDesc )

You may want to read the article on Regular Expressions to better understand how these work in Descriptive Programming.

After executing the snippet above, we will have a collection object with all object that match the regular expression. Let’s see the count of the objects:

'Returns 1 because our supplied pattern only matches with the word: ImagesMsgBox colObject.Count

Let’s replace the “text” with “ma” instead and see if we still retrieve a collection with a single object:

Dim oDesc 'Description ObjectDim colObject 'Object CollectionSet oDesc = Description.CreateoDesc( "micclass" ).value = "Link"oDesc( "text" ).value = ".*ma.*" 'Images'.regularExpression is 'True' by defaultoDesc( "text" ).regularExpression = True Set colObject = Browser( "title:=Google").Page("title:=Google").ChildObjects( oDesc )

To see what has changed, let’s execute the following:

'Will return 4

Page 60: Releavantcodes

MsgBox colObject.Count

If you do execute the snippet above, the count would be 4. Why is that? To see the new objects in our collection, let’s do this:

For x = 0 to colObject.Count - 1MsgBox "x:" & x & " || " & colObject(x).GetROProperty("innertext")

Next

Below are our outputs:

Interesting, isn’t it? Our description for the text property matched all links that had the letters ma in them.

V. Negating WildCardsSometimes we have an object description with wildcards in it. Here, we want to turn off the Description Object’s ability to register the description as a regular expression. This is quite simple, and we do it by turning regular expression as False:

Dim oDesc 'Description ObjectDim colObject 'Object Collection

Set oDesc = Description.CreateoDesc( "micclass" ).value = "Link"oDesc( "text" ).value = ".*ma.*" 'ImagesoDesc( "text" ).regularExpression = False Set colObject = Browser( "title:=Google").Page("title:=Google").ChildObjects( oDesc ) MsgBox colObject.Count 'Returns 0

Now, the above snippet will not identify Images or Maps. Instead, it will only identify objects that have the exact text .*ma.* which really doesn’t exist anywhere on the Google homepage.

VI. Creating a Description Object for Integer-TypesSo far, we have only learned various ways to work with description objects using Strings. Here, we will see how they transform into integer-types. Let’s bring up the properties of the Images link again using the Object spy and feed its “x” coordinate to see whether we can find the link:

Page 61: Releavantcodes

Object Spy Snapshot: Images Link (Google)

Dim oDesc 'Description ObjectDim colObject 'Object Collection

Set oDesc = Description.CreateoDesc( "micclass" ).value = "Link"oDesc( "x" ).value = 51 'It should always be 51, not "51".

Set colObject = Browser( "title:=Google").Page("title:=Google").ChildObjects( oDesc ) 'Will return 1MsgBox colObject.Count 'Will return ImagesFor x = 0 to colObject.Count - 1

MsgBox colObject(x).GetROProperty("innertext")Next

Notice that 51 above is not within quotation marks. This should always be the case. Notice when we replace it with a string-type below:

Dim oDesc 'Description ObjectDim colObject 'Object Collection

Set oDesc = Description.CreateoDesc( "micclass" ).value = "Link"oDesc( "x" ).value = "51" Set colObject = Browser( "title:=Google").Page("title:=Google").ChildObjects( oDesc ) MsgBox colObject.Count For x = 0 to colObject.Count - 1

MsgBox colObject(x).GetROProperty("innertext")Next

Page 62: Releavantcodes

The collection is an empty one:

(Mis)Using Integers as Strings

So, always remember to treat integers as they are. This always holds true for x, y, abs_x, abs_y.

VII. Building Object hierarchy with ChildObjectsWe will use the Browser Object Collection to set a value in a text box to demonstrate this:

Dim oDesc 'Description ObjectDim colObject 'Object CollectionSet oDesc = Description.CreateoDesc( "micclass" ).value = "Browser"oDesc( "title" ).value = "Google" 'Notice we used the Desktop Object again for retrieving Window collectionSet colObject = Desktop.ChildObjects( oDesc ) 'Retrieve the countiCount = colObject.Count 'Set value in the WebEdit of the last Window object in the collectionFor x = 0 to iCount - 1 'Verify if the Browser title equals "Google" If Browser("creationtime:=" & x).GetROProperty("title") = "Google" Then 'Set Description.Create in the search TextBox Browser("creationtime:="&x).Page("micclass:=Page").WebEdit("name:=q").Set "Description.Create" End IfNext

When the above snippet executes, we will have the following value set in the Search TextBox in Google HomePage:

Using Description.Create to Set Value

Page 63: Releavantcodes

I hope this article will help you guys understand the concepts of one of the most important concepts in Descriptive Programming: the Description Object. Thanks for visiting Relevant Codes

QTP: Create Test Archives This topic discusses archiving files created during a test session. These can be text files, XMLs, custom snapshots etc. The important bit is to keep them in one centralized test folder. At my current project, one of the core requirements of any automated test-suite is capturing snapshots wherever required by the user. Because each suite has several test scripts, we end up collecting hundreds of snapshots in each cycle. This is where archives come in – we create archives for each script, making it easier for the users to refer to the correct folder for the test and the time the test was run.

Assume you have a folder ‘Test XYZ’ is where you are storing all the runtime files and snapshots:

Z:\Automated Scripts\QuickTest Professional\Some Application\Image Captures\Test XYZ\

Using the technique below, you can create quick archives for each test session. I usually do this at the end of my tests, but this procedure can be called anywhere in the script. Assume (again) you have the following file structure during a run-session:

Before Archiving

After running the following line of code:

'C:\Demo is the parent test folderCall CreateTestArchive("C:\Demo")

You will see a neat archive folder with the correct timestamp:

Page 64: Releavantcodes

After Archiving

The contents of the newly created folder (005) will now carry all the files that were in the parent folder:

Archive Folder- Contents

How it’s done:

'-------------------------------------------' Name: function CreateTestArchive' ' Purpose: Creates an archive folder with a date-time stamp'' Input(s):' sPath - Path of the parent folder where all the files are located'' Output:' ""'-------------------------------------------function CreateTestArchive(sPath)'-------------------------------------------

Dim oFSO, oFolder, colSubFolder, sFolder 

'FileSystemObjectSet oFSO = CreateObject("Scripting.FileSystemObject")'Bind to the supplied pathSet oFolder = oFSO.GetFolder(sPath)'Retrieve collection of all subfoldersSet colSubFolder = oFolder.SubFolders

 'Create numbering for the new folderSelect Case Len(CStr(colSubFolder.Count))

Case 1: sFolder = "00"Case 2: sFolder = "0"Case Else: sFolder = ""

End Select 

'New folder name: this part can be modified so naming convention complies

' to your guidelines.'Example: 009 - Archive Aug, 09 2009 3.05.02 PMsFolder = sFolder & colSubFolder.Count + 1 & " - Archive " & _

Page 65: Releavantcodes

ParseDate(Date, "MMM, dd yyyy") & " " & Replace(Time, ":", ".")  'Move all files to the new folder

If oFolder.Files.Count > 0 ThenIf Not InStr(Len(sPath), sPath, "\") <> 0 Then sPath = sPath &

"\"'Create the new folderoFSO.CreateFolder sPath & sFolderoFSO.MoveFile sPath & "*.*", sPath & sFolder

End If 

Set oFSO = NothingSet oFolder = NothingSet colSubFolder = Nothing

end function

The snippet below parses the date to a desired format. I am using MM, dd yyyy, but you can modify this to support any format, or multiple formats:

'-------------------------------------------' Name: function ParseDate'' Purpose: Uses DOTNetFactory to parse date in desired format'' Input(s):' dtDate: Date that is to be parsed' sFormat: Format in which the date is to be parsed'' Output:' Date'-------------------------------------------function ParseDate(dtDate, sFormat)'-------------------------------------------

Dim oInstance 

Set oInstance = DotNetFactory.CreateInstance("System.DateTime")ParseDate = oInstance.Parse(CStr(dtDate)).ToString(sFormat)

 Set oInstance = Nothing

end function

I hope you will like this simple approach. Thanks for visiting Relevant Codes :)

QTP 9.2: Delay Using CreationTime:=0 With 1 Open Browser For those who use the CreationTime property extensively, you might have noticed a 10-15 seconds delay when working with only 1 open browser. This delay can be avoided, and the answer is quite simple. I am not sure why this approach works whereas CreationTime:=0 fails, but when I find out, I will be sure to update this post. I know that versions 9.5 and 10.0 are being used in many shops, and this is no longer a limitation, but for those who are still using v9.2 (my favorite), this article may help you overcome this issue.

Page 66: Releavantcodes

Solution: Instead of using 0 for Browser CreationTime, use Null. In other words, instead of the usual:

Browser("creationtime:=0")

replace the 0 with a null character:

Browser("creationtime:=")

To test this, open a single browser, and execute the lines below:

Dim timerInit, timerEnd timerInit = TimerBrowser("creationtime:=0").HighlighttimerEnd = Timer - timerInit MsgBox "With 'CreationTime:=0' : " & timerEnd & " seconds." timerInit = TimerBrowser("creationtime:=").HighlighttimerEnd = Timer - timerInit MsgBox "With 'CreationTime:=' : " & timerEnd & " seconds."

Result:

CreationTime = 0

CreationTime = ""

Descriptive Programming (DP) – 4 (Creating a Test Script) This is the last article in our Descriptive Programming series and will cover a simple login process using 100% DP. I have purposely created the example to be very high-level to make sure its quit easy to understand. However, if you feel more examples on this concept will help, I’ll be more than happy to create a Part V of this series with only real-world examples of DP in action. We will use the HP/Mercury Demo Website for this example.Following is the process I am going to follow to complete this process. In your application however, you can use the process that best suits your needs, but for the purposes of this lesson, I will keep it quite basic:

1. Launch Browser. 2. Check whether the correct browser opened.

Page 67: Releavantcodes

3. Ensure the userName, password edits and the Sign-In button exist. 4. Set the userName and password and Click Sign-In. 5. Make sure the browser navigated to the correct page.

Step 1: Launch Browser

'We will use SystemUtil.Run to launch our target browserSystemUtil.Run "iexplore.exe", "http://newtours.demoaut.com/"

Step 2: Checking if the correct browser opened

The 2 new concepts in this step are:

1. Reporter Object: This object is used to send individual reports to the test results. In other words, when the test ends, you can see the reported events in your Test results.

2. ExitTest: A utility statement available to QTP that enables it to complete exit a Test. In other words, when this statement executes, the test execution ends.

If Browser( "title:=Welcome: Mercury Tours" ).Exist( 15 ) Then Reporter.ReportEvent micPass, "Step 1- Launch", "Correct browser was launched."Else Reporter.ReportEvent micFail, "Step 1- Launch", "Browser failed to launch." ExitTestEnd If

Step 3: Ensure the userName, password edits and the Sign-In button exist.

'Notice the use of the wildcard character belowIf Browser("title:=Welcome:.*").Page("title:=Welcome.*").WebEdit("name:=userName").Exist(0) Then 'set username If Browser("title:=Welcome:.*").Page("title:=Welcome.*").WebEdit("name:=password").Exist(0) Then 'set password If Browser("title:=Welcome:.*").Page("title:=Welcome.*").Image("name:=login" ).Exist(0) Then 'click button Else 'report that Sign-In button was not found End If Else 'report that the password edit was not found End IfElse 'report that the userName edit was not found

Page 68: Releavantcodes

End If

Step 4: Set the userName and password and Click Sign-In.

The following will complete the snippet above:

'Notice the use of the wildcard character belowWith Browser("title:=Welcome:.*").Page("title:=Welcome.*") If .WebEdit("name:=userName").Exist(0) Then .WebEdit("name:=userName").Set "test" If .WebEdit("name:=password").Exist(0) Then .WebEdit("name:=password").Set "test" If .Image("name:=login" ).Exist(0) Then .Image("name:=login" ).Click Else Reporter.ReportEvent micFail, "Sign-In Button Error", "Button not found." End If Else Reporter.ReportEvent micFail, "Password Edit Error", "EditBox not found." End If Else Reporter.ReportEvent micFail, "UserName Edit Error", "EditBox not found." End IfEnd With

Step 5: Make sure the browser navigated to the correct page

'Synchronize with a browserBrowser( "title:=.*" ).Sync 'Wait 1 second for browser with "Find a Flight" title to existIf Browser( "title:=Find a Flight.*" ).Exist( 1 ) Then Reporter.ReportEvent micPass, "Login", "Login successful"Else Reporter.ReportEvent micFail, "Login", "Login failed"End If

Putting it all together

'We will use SystemUtil.Run to launch our target browserSystemUtil.Run "iexplore.exe", "http://newtours.demoaut.com/" If Browser( "title:=Welcome: Mercury Tours" ).Exist( 15 ) Then Reporter.ReportEvent micPass, "Step 1- Launch", "Correct browser was launched."Else Reporter.ReportEvent micFail, "Step 1- Launch", "Browser failed to launch." ExitTestEnd If 'Notice the use of the wildcard character belowWith Browser("title:=Welcome:.*").Page("title:=Welcome.*") If .WebEdit("name:=userName").Exist(0) Then

Page 69: Releavantcodes

.WebEdit("name:=userName").Set "test" If .WebEdit("name:=password").Exist(0) Then .WebEdit("name:=password").Set "test" If .Image("name:=login").Exist(0) Then .Image("name:=login").Click Else Reporter.ReportEvent micFail, "Sign-In Button Error", "Button not found." End If Else Reporter.ReportEvent micFail, "Password Edit Error", "EditBox not found." End If Else Reporter.ReportEvent micFail, "UserName Edit Error", "EditBox not found." End IfEnd With Browser( "title:=.*Mercury.*" ).Sync If Browser( "title:=Find a Flight.*" ).Exist( 1 ) Then Reporter.ReportEvent micPass, "Login", "Login successful"Else Reporter.ReportEvent micFail, "Login", "Login failed"End If

If you have any doubt in the content above, please feel free to post a comment about it. I hope this article helps understand further the principles of Descriptive Programming and using it in your everyday work.

Descriptive Programming (DP) Concepts – 3 (Ordinal Identifiers) This is the third article in the Descriptive Programming series, and will outline the concepts of Ordinal Identifiers used in QTP. We will also create a simple test module (step by step) for a login process using only Descriptive Programming (DP).

Ordinal Identifiers – What are they?Let me quote QTP Reference here:

An ordinal identifier assigns a numerical value to a test object that indicates its order or location relative to other objects with an otherwise identical description (objects that have the same values for all properties). This ordered value provides a backup mechanism that enables QuickTest to create a unique description to recognize an object when the defined properties are not sufficient to do so. Let’s break the above definition from Mercury/HP into several parts to clarify the concept. An ordinal identifier assigns a numerical value to a test object. From the quote above, we can conclude that an ordinal identifier is a numerical entity. In other words, its simply a number that is assigned to a test object. that indicates its order or location relative to other objects with an otherwise identical description (objects that have the same values for all properties)

Page 70: Releavantcodes

This means, an Ordinal Identifier works quite differently in relation to the properties we learned in the 1st part of this series. This identifier, or a property if you will, works according to the order or location of test objects. Objects’ order and location are unique characteristics. For example, in a coordinate system, generally only a single object exists on a given ‘x,y’ coordinate. Thus, an ordinal identifier will always be unique. Index defines the order, and location defines location. This ordered value provides a backup mechanism that enables QuickTest to create a unique description to recognize an object when the defined properties are not sufficient to do so.

The quote above is a good way to conclude this concept of Ordinal Identifiers in QTP. Since it is always unique for an object, it can become extremely useful including these with objects’ mandatory and assisstive properties to prevent falling into object recognition problems. The 3 types of ordinal identifiers are: Location, Index and CreationTime (browser only).

Location Ordinal IdentifierLet’s use an example to understand how the Location Identifier works. Consider the 4 WebEdits below:

Text Box 1:

  

Text Box 2:

Text Box 3:

  

Text Box 4:

All the edits above have exactly the same properties. This property works vertically, from top to bottom. Thus, ‘Text Box 1‘ will have a location value of 0, ‘Text Box 3‘ will have 1, ‘Text Box 2‘ will have 2, and ‘Text Box 4‘ will have 3. Note that VBScript is zero based, so the location property would start at 0. This can be verified by running the following statements:

'Text Box 1Browser("title:=.*Descriptive.*").Page("micclass:=Page").WebEdit("name:=dpTest","location:=0").Set "1"'Text Box 3Browser("title:=.*Descriptive.*").Page("micclass:=Page").WebEdit("name:=dpTest","location:=1").Set "2"'Text Box 2Browser("title:=.*Descriptive.*").Page("micclass:=Page").WebEdit("name:=dpTest","location:=2").Set "3"'Text Box 4

Browser("title:=.*Descriptive.*").Page("micclass:=Page").WebEdit("name:=dpTest","location:=3").Set "4"Text Box 1: location=0Text Box 2: location=2Text Box 3: location=1Text Box 4: location=3

Page 71: Releavantcodes

Index Ordinal IdentifierIndex is quite similar to location, but it works from left to right instead of top to bottom. Thus, for the same group of edit boxes above: ‘Text Box 1′ will have an index of 0, ‘Text Box 2′ will have 1, ‘Text Box 3′ will have 2 and ‘Text Box 4′ will have 3. Let’s test our statements:

'Text Box 1Browser("title:=.*Descriptive.*").Page("micclass:=Page").WebEdit("name:=dpTest", "index:=0").Set "1" 'Text Box 2Browser("title:=.*Descriptive.*").Page("micclass:=Page").WebEdit("name:=dpTest", "index:=1").Set "2" 'Text Box 3Browser("title:=.*Descriptive.*").Page("micclass:=Page").WebEdit("name:=dpTest", "index:=2").Set "3" 'Text Box 4Browser("title:=.*Descriptive.*").Page("micclass:=Page").WebEdit("name:=dpTest", "index:=3").Set "4"Text Box 1: index=0Text Box 2: index=1Text Box 3: index=2Text Box 4: index=3

That was quite easy, wasn’t it? Now, let’s move on to CreationTime, which is an ordinal identifier strictly reserved for the browser object.

CreationTime Ordinal Identifier .Again, let’s use the description given by HP/Mercury in QTP’s helpfile:

If there are several open browsers, the one with the lowest CreationTime is the first one that was opened and the one with the highest CreationTime is the last one that was opened. That means, the first browser you open will have a creationtime of 0. The second browser will have a creationtime of 1. The third browser will have a creationtime of 2 and so on.

SystemUtil.Run "iexplore.exe", "http://www.HP.com" 'CreationTime 0

SystemUtil.Run "iexplore.exe", "http://www.AdvancedQTP.com" 'CreationTime 1SystemUtil.Run "iexplore.exe", "http://www.LinkedIn.com" 'CreationTime 2Browser( "creationtime:=" ).Highlight 'Highlight HP.comBrowser( "creationtime:=1" ).Highlight 'Highlight AdvancedQTP.comBrowser( "creationtime:=2" ).Highlight 'Highlight LinkedIn.com

When you run the above code in QTP, you will find that the first browser QTP highlights on is HP.com, the second is AdvancedQTP.com and the third is LinkedIn.com. Even this is quite simple, isn’t it? As promised, we must create a simple login process using the concepts we have learned so far in the next article.

Page 72: Releavantcodes

Descriptive Programming (DP) vs Object Repository (OR) DP vs OR: Which is better? This has been a never ending debate and if you follow QTP forums, you will see a thread pop-up every few days. Most commonly, the OP is trying to inquire which one would be better (vague?), which one would require less maintenance time, which one is more user-friendly, which has the performance upside, which one will my boss admire more etc.

The answer isn’t simple. That’s why this has been a never ending debate :)

I do not guarantee that after reading this post, you will have an answer, but I am sure with some planning, you will be able to make up your mind as to which one would be the better alternative in the long run.

1. In Expert View, DP and OR statements are written in the following manner:

'ORBrowser( "Google" ).Page( "Google" ).WebEdit( "q" ).Set "Google Search" 'DPBrowser( "title:=Google" ).Page( "title:=Google" ).WebEdit( "name:=q" ).Set "Google Search"

With DP, the properties are written with their corresponding values. OR, on the other hand, shows only a logical description of the object.

2. A DP parent will only recognize a child that has DP style layout. In other words, you CANNOT add a child to a DP parent that has its properties stored in the OR. Example:

'Will not work: Browser is DP whereas Page and WebEdit are ORBrowser( "title:=Google" ).Page( "Google" ).WebEdit( "q" ).Set "Google Search"

 'Will not work: Browser and Page are DP whereas WebEdit is ORBrowser( "title:=Google" ).Page( "title:=Google" ).WebEdit( "q" ).Set "Google Search"

However, an OR parent will recognize both OR and DP style children:

'Will work: Browser is OR whereas Page and WebEdit are DPBrowser( "Google" ).Page( "title:=Google" ).WebEdit( "name:=q" ).Set "Google Search" 'Will work: Browser and Page are OR whereas WebEdit is DPBrowser( "Google" ).Page( "Google" ).WebEdit( "name:=q" ).Set "Google Search"

3. Writing good DP for Objects takes experience and skill. Even though DP has become extremely popular, improperly written DP can lead to performance issues, maintenance headaches etc. Its quite a different story with OR where you record the objects and QTP creates the hierarchy.

Page 73: Releavantcodes

4. The user must be careful when using DP: it is case sensitive (except in the case of Web Applications). Do not confuse this with how VBScript works. If the value of a property ‘html id’ is ‘txtZip’ then it must be coded as ‘txtZip’, not ‘txtzip’ or ‘TXTZIP’. QTP will only find the object if the value is written exactly as in the Object Spy. Example (WebEdit’s name property is retrieved as ‘editNewName’):

'Correct description for WebEditWindow("nativeclass:=Test").WinEdit( "name:=editNewName" ).Set "DP" 'Incorrect description for WebEditWindow("nativeclass:=Test").WinEdit( "name:=editnewname" ).Set "DP" 'Incorrect description for WebEditWindow("nativeclass:=Test").WinEdit( "name:=EDITNEWNAME").Set "DP"

5.Unlike OR, DP does not have auto-complete. All object descriptions must be coded out manually. This can be quite time consuming, and even frustrating for people who’re used to working with OR.

Object Repository Auto Complete Feature

6.Unlike OR’s ‘Highlight in Application’ feature, when searching for an object hierarchy written in DP, you must execute the line of code to locate the object in the application. Another way would be to keep a record of all objects, which can be quite tedious.

Object Repository Highlight in Application Feature

7. DP supports ChildObjects (to create the description object, you MUST use DP). Description object example:

Dim oDesc Set oDesc = Description.Create'Add a name property to the descriptionoDesc( "name" ).Value = "q"'Add a html tag property to the descriptionoDesc( "html tag" ).Value = "INPUT"

8. On average, DP is slower than OR (note: on average!). With DP, QTP creates the an object from the provided hierarchy of DP and locates the object in the application before performing an event. Demo:

Page 74: Releavantcodes

'OR: 9.74 secondsFor i = 0 to 99

Browser("Google").Page("Google").WebEdit("q").Set "QTP"Next 'DP: 14.64 secondsFor i = 0 to 99

Browser("title:=Google").Page("title:=Google").WebEdit("name:=q").Set "QTP"Next

9. OR has a GUI which makes is easy to understand object hierarchies and maintain objects. DP hierarchies are code statements. Example:

Object Repository Front End GUI

WhiteBox style DP

10. .Object is available to both DP and OR:

'ORBrowser("Google").Page("Google").WebEdit("q").Object.Value = "QTP" 'DPBrowser("title:=Google").Page("title:=Google").WebEdit("name:=q").Object.Value = "QTP"

11. Objects have logical names in OR which makes it easy to identify them. Sadly, DP doesn’t, since its based on principles of a white-box.

12. This is strictly for OR, but at times you will notice that when working with applications, objects are added as: WebEdit_1, WebEdit_2, WebEdit_3 … WebEdit_n and so on. This can be extremely cumbersome to deal with, and it takes quite long to rename all the objects in OR to give them meaningful names.

13. Code written with DP can be easily copied/pasted onto other scripts or functions. With OR based scripts, a simple copy/paste will not result in a working script. All the repositories (local/shared) would have to be associated with the test to make it executable. Therefore, when creating test with many reusable components, its generally easier to work with DP in comparison to OR unless there is a comprehensive use of Shared Object Repositories [Credits: Yogindernath.

Page 75: Releavantcodes

14. Object Repositories have capabilities to perform single-point maintenance, whereas with DP, during changes, all objects undergo manual updates. In situations where there are several changes in the AUT, OR can prove to be a better performer [Credits: Marc Miedemo]If you feel I have missed something and must be added or corrected, please post a comment and I will update this post, giving full credit to you. Thanks :)

QTP: Verify Multiple Object Properties – An Elegant Approach This article discusses an elegant way of cross-checking expected vs actual properties of objects. The two important components of this technique are:

1. Modular Classes 2. DoPropertiesConform Method

Modular ClassesThe main concept behind this technique is the usage of Dictionaries. There are 2 types of Dictionaries used in Modular Classes: a Base (Parent) Dictionary and one (or more) Child Dictionaries. All Child Dictionaries are stored in a single Base Dictionary and passed through the class method. This means, if we’re checking object properties in the LogIn page, the Login Class will contain a Base Dictionary and several Child Dictionaries. If we’re checking properties of 2 objects in the LogIn page, there will be 2 Child Dictionaries (1 for each object) and a Base Dictionary. Example:

Class clsLoginPage  ' Private and Public Variables ' Other Properties and Methods

Public Function Collection Dim BaseDictionary 'A Single Parent Dictionary Dim ChildDictionary_1 '1st Child Dictionary Dim ChildDictionary_2 '2nd Child Dictionary Set BaseDictionary = CreateObject( "Scripting.Dictionary" ) Set ChildDictionary_1 = CreateObject( "Scripting.Dictionary" ) Set ChildDictionary_2 = CreateObject( "Scripting.Dictionary" ) End Function End Class

Above, we created 3 dictionaries: 1 Parent and 2 Child. Now, let’s add some properties to the Child Dictionaries:

Class clsLoginPage  ' Private and Public Variables ' Other Properties and Methods Public Function Collection Dim BaseDictionary 'A Single Parent Dictionary Dim ChildDictionary_1 '1st Child Dictionary

Page 76: Releavantcodes

Dim ChildDictionary_2 '2nd Child Dictionary Set BaseDictionary = CreateObject( "Scripting.Dictionary" ) Set ChildDictionary_1 = CreateObject( "Scripting.Dictionary" ) Set ChildDictionary_2 = CreateObject( "Scripting.Dictionary" )  With ChildDictionary_1 .Add "Me", Browser("title:=Google").Page("micclass:=Page").WebEdit("name:=q") .Add "html tag", "INPUT" .Add "width", 100 End With  With ChildDictionary_2 .Add "Me", Browser("title:=Google").Page("micclass:=Page").Image("file name:=logo.gif") .Add "html tag", "IMG" .Add "width", 50 End With End Function End Class

Above, we added properties to the 2 Child Dictionaries. Please note that, the very first item/key in the dictionary in the Child Dictionaries is the object itself. I have used “Me” denoting that it is the object itself; you can change “Me” to any other descriptive term. However, the first item in the Collection must always be the object. Finally, let’s pass the Child Dictionaries through the method with the help of the Parent Dictionary:

Class clsLoginPage  ' Public and Private Variables Public Function Collection Dim BaseDictionary 'A Single Parent Dictionary Dim ChildDictionary_1 '1st Child Dictionary (Search WebEdit) Dim ChildDictionary_2 '2nd Child Dictionary (SiteLogo Image) Set BaseDictionary = CreateObject( "Scripting.Dictionary" ) Set ChildDictionary_1 = CreateObject( "Scripting.Dictionary" ) Set ChildDictionary_2 = CreateObject( "Scripting.Dictionary" )  With ChildDictionary_1 .Add "Me", Browser("title:=Google").Page("micclass:=Page").WebEdit("name:=q") .Add "html tag", "INPUT" .Add "width", 100 End With  With ChildDictionary_2 .Add "Me", Browser("title:=Google").Page("micclass:=Page").Image("file name:=logo.gif") .Add "html tag", "IMG" .Add "width", 50 End With  With BaseDictionary .Add "Search", ChildDictionary_1 .Add "SiteLogo", ChildDictionary_2

Page 77: Releavantcodes

End With  Set Collection = BaseDictionary End Function  ' Other Methods and Properties End Class

Above, we passed the Parent dictionary containing both child dictionaries through the method. One last thing here. We must release all objects when we’re done (we will have to declare the variables in the Class scope, instead of method scope):

Class clsLoginPage  Private BaseDictionary 'Parent Dictionary (Class Scope) Private ChildDictionary_1 'Child Dictionary 1 (Class Scope) Private ChildDictionary_2 'Child Dictionary 2 (Class Scope) Public Function Collection Set BaseDictionary = CreateObject( "Scripting.Dictionary" ) Set ChildDictionary_1 = CreateObject( "Scripting.Dictionary" ) Set ChildDictionary_2 = CreateObject( "Scripting.Dictionary" )  With ChildDictionary_1 .Add "Me", Browser("title:=Google").Page("micclass:=Page").WebEdit("name:=q") .Add "html tag", "INPUT" .Add "width", 100 End With  With ChildDictionary_2 .Add "Me", Browser("title:=Google").Page("micclass:=Page").Image("file name:=logo.gif") .Add "html tag", "IMG" .Add "width", 50 End With  With BaseDictionary .Add "Search", ChildDictionary_1 .Add "SiteLogo", ChildDictionary_2 End With  Set Collection = BaseDictionary End Function  Public Sub Release Set BaseDictionary = Nothing Set ChildDictionary_1 = Nothing Set ChildDictionary_2 = Nothing End Sub  ' Private and Public Variables ' Other Properties and Methods End Class

Now let’s cover the ‘DoPropertiesConform’ method of this technique.

Page 78: Releavantcodes

DoPropertiesConformThis is where all the magic happens (simply keep it in one of your Global libraries and you’re good to go). This method verifies all the properties stored in the Dictionary with the runtime object properties. The final results are reported through Reporter events as HTML tables. The input to this method is the parent dictionary.

'————————————————————————————————————————————— ' Name: Function DoPropertiesConform' Purpose: Checks if the expected and actual properties match' Input:' m_htContextBase (Base Collection - this contains several child collections)' Output: ' Boolean' Date:' Author: Anshoo Arora'————————————————————————————————————————————— Public Function DoPropertiesConform( ByVal m_htContextBase )'————————————————————————————————————————————— Dim vKeys, intStatus, i, m_ContextTemp, bResult, keysTemp, oTemp, x, sStatus  DoPropertiesConform = False  vKeys = m_htContextBase.Keys intStatus = micPass  For i = 0 to m_htContextBase.Count-1 ' The below will create temporary references to Collection ' In our test, the Collection contains two objects: "Logo" and "UserName" Set m_ContextTemp = m_htContextBase.Item(vKeys(i)) bResult = bResult & "&lt;<table border='1'><tr><td>Status</td><td>Property</td><td>Expected</td><td>Actual</td></tr>" keysTemp = m_ContextTemp.Keys If m_ContextTemp.Item(keysTemp(0)).Exist(0) Then ' Create a temporary reference to the object in the base collection ' Key values for these objects must always start with "Me" Set oTemp = m_ContextTemp.Item(keysTemp(0)) For x = 1 to m_ContextTemp.Count - 1 sStatus = "Pass" If Not CStr(oTemp.GetROProperty(keysTemp(x))) = CStr(m_ContextTemp.Item(keysTemp(x))) Then intStatus = micWarning sStatus = "Warning" End If bResult = bResult & "<tr><td>"&sStatus&"</td><td>"&KeysTemp(x)&"</td><td>"&m_ContextTemp.Item(keysTemp(x))&"</td><td>"&oTemp.GetROProperty(keysTemp(x))&"</td></tr>" Next Set m_ContextTemp = Nothing bResult = bResult & "</table>&gt;" & vbLf

Page 79: Releavantcodes

End If Next Reporter.ReportEvent intStatus, "DoPropertiesConform", bResult  If intStatus = micPass Then DoPropertiesConform = True  Set m_htContextBase = Nothing Set m_ContextTemp = NothingEnd Function

Once we have added all the relevant objects and their properties to the parent dictionary, it just takes the following line of code to run all the verifications:

Call DoPropertiesConform(ParentDictionary)

ExamplesIn the 2 examples below, we will verify expected and actual properties of the Site Logo and the UserName WebEdit for the Mercury/HP Demo Web Application. Below are the snapshots of the 2 objects verified in the examples:

Site Logo

Mercury Tours: UserName WebEdit

Example 1: Case where all properties match (Output = Pass)Option Explicit

 Class clsHomePageObjects  Private m_ContextBase 'Parent Private m_ContextLogo 'Child 1 Private m_ContextUserName 'Child 2 Public Function Collection ' Parent Dictionary Set m_ContextBase = CreateObject( "Scripting.Dictionary" )  ' Child Dictionaries: m_ContextLogo & m_ContextUserName Set m_ContextLogo = CreateObject( "Scripting.Dictionary" ) Set m_ContextUserName = CreateObject( "Scripting.Dictionary" )  ' Properties of the Object: Site Logo Image With m_ContextLogo

Page 80: Releavantcodes

' Actual Object is the first Item/Key .Add "Me", Browser( "title:=Welcome: Mercury Tours" ).Image( "file name:=logo.gif" ) .Add "alt", "Mercury Tours" .Add "file name", "logo.gif" .Add "height", "110" .Add "name", "Image" .Add "width", "100" End With  ' Properties of the Object: UserName WebEdit With m_ContextUserName ' The first Item/Key must always contain the object .Add "Me", Browser( "title:=Welcome: Mercury Tours" ).WebEdit( "name:=userName" ) .Add "disabled", "0" .Add "height", "22" .Add "kind", "singleline" .Add "name", "userName" .Add "readonly", "0" .Add "rows", "0" .Add "type", "text" .Add "width", "86" End With  ' The Parent Dictionary stores both Child Dictionaries With m_ContextBase .Add "Logo", m_ContextLogo .Add "UserName", m_ContextUserName End With  Set Collection = m_ContextBase End Function  Public Sub Release Set m_ContextBase = Nothing Set m_ContextLogo = Nothing Set m_ContextUserName = Nothing End Sub  ' Private and Public Variables ' Other Properties and Methods End Class ' Usage:Dim oNew: Set oNew = New clsHomePageObjectsDoPropertiesConform oNew.CollectionoNew.Release

Page 81: Releavantcodes

All Checks Passed

In the snapshot above, you will notice that all properties for the site_logo and the userName webEdit passed.

Example 2: Case where some properties match (Output = Fail)Option Explicit

 Class clsHomePageObjects  Private m_ContextBase 'Parent Private m_ContextLogo 'Child 1 Private m_ContextUserName 'Child 2 Public Function Collection ' Parent Dictionary Set m_ContextBase = CreateObject( "Scripting.Dictionary" )  ' Child Dictionaries: m_ContextLogo & m_ContextUserName Set m_ContextLogo = CreateObject( "Scripting.Dictionary" ) Set m_ContextUserName = CreateObject( "Scripting.Dictionary" )  ' Properties of the Object: Site Logo Image With m_ContextLogo ' Actual Object is the first Item/Key .Add "Me", Browser( "title:=Welcome: Mercury Tours" ).Image( "file name:=logo.gif" ) .Add "alt", "Mercury Tours" .Add "file name", "logo.gif" .Add "height", "100" 'Will Fail .Add "name", "Image" .Add "width", "100" End With  ' Properties of the Object: UserName WebEdit With m_ContextUserName ' The first Item/Key must always contain the object

Page 82: Releavantcodes

.Add "Me", Browser( "title:=Welcome: Mercury Tours" ).WebEdit( "name:=userName" ) .Add "disabled", "0" .Add "height", "21" 'Will Fail .Add "kind", "singleline" .Add "name", "userName" .Add "readonly", "0" .Add "rows", "0" .Add "type", "text" .Add "width", "86" End With  ' The Parent Dictionary stores both Child Dictionaries With m_ContextBase .Add "Logo", m_ContextLogo .Add "UserName", m_ContextUserName End With  Set Collection = m_ContextBase End Function  Public Sub Release Set m_ContextBase = Nothing Set m_ContextLogo = Nothing Set m_ContextUserName = Nothing End Sub  ' Private and Public Variables ' Other Properties and Methods End Class ' Usage:Dim oNew: Set oNew = New clsHomePageObjectsDoPropertiesConform oNew.CollectionoNew.Release

Some Checks Failed

In the snapshot above, you will notice that there are 2 Warning statuses, one for each object.

I hope you guys find this article useful :)

Page 83: Releavantcodes

Descriptive Programming (DP) Concepts – 2 {Regular Expressions}

A wildcard character can be used to substitute for any other character or characters in a string.1 This means, we can use a wildcard to make our descriptions more generic. For example, if the property ‘file name’ of an Image is ‘getAllAttributes.JPG’, we can use a wildcard several ways:

' Only using the first 2 words: getAllBrowser( "title:=MyTitle" ).Page( "title:=MyTitle" ).Image( "file name:=getAll.*" ).Click' Using 1 word (Attributes) with the extension (JPG)Browser( "title:=MyTitle" ).Page( "title:=MyTitle" ).Image( "file name:=.*Attributes.*JPG" ).Click

Let’s put this technique into practice. Let’s use Mercury Tours for this test. Let’s try to identify the banner image (banner2.gif) having the following text embed: ‘one cool summer ARUBA’.

This image is the property of http://newtours.demoaut.com (HP/Mercury)

Browser("title:=Welcome: Mercury Tours").Page("title:=Welcome: Mercury Tours").Image("file name:=banner2.gif").HighlightBrowser("title:=Welcome: Mercury Tours").Page("title:=Welcome: Mercury Tours").Image("file name:=banner2.*").HighlightBrowser("title:=Welcome: Mercury Tours").Page("title:=Welcome: Mercury Tours").Image("file name:=banner.*").HighlightBrowser("title:=Welcome: Mercury Tours").Page("title:=Welcome: Mercury Tours").Image("file name:=ban.*gif").HighlightBrowser("title:=Welcome: Mercury Tours").Page("title:=Welcome: Mercury Tours").Image("file name:=ban.*f").Highlight

Ofcourse, there are more ways to identify the banner image, but I’ve only used the above 5. Similarly, we can use this wildcard character for the Browser and Page objects:

' Without wildcard(s): 0.20 secondsBrowser("title:=Welcome: Mercury Tours").Page("title:=Welcome: Mercury Tours").Image("file name:=banner2.gif").Click ' With wildcard(s): 0.30 secondsBrowser("title:=Welcome.*").Page("title:=.*Mercury Tours").Image("file name:=banner2.gif").Highlight  ' 0.28 secondsBrowser("title:=Welcome:.*").Page("title:=Welcome.*").Image("file name:=banner2.*").Highlight  ' 0.56 secondsBrowser("title:=.*Mercury Tours").Page("title:=.*: Mercury.*").Image("file name:=banner.*").Highlight  ' 0.61 secondsBrowser("title:=.*Mercury Tour.*").Page("title:=Welcome:.*").Image("file name:=ban.*gif").Highlight

Page 84: Releavantcodes

 ' 0.51 secondsBrowser("title:=.*: Mercury.*").Page("title:=.*Mercury Tour.*").Image("file name:=ban.*f").Highlight

You might notice a little drop in performance for some of the above statements. This is quite obvious though. Using a real world example:

Scenario 1 : If you were asked to deliver a letter to John and you had the following piece of information provided: Building 184, Floor 5, Room 120, Desk 9. You would know that you first have to find Building A, then take an elevator to the 5th floor, find Room 120, and once you’re inside room 120, John sits on Desk # 9. This is quite straight-forward and ofcourse you’ll be able to quickly find John.

Scenario 2 : In another scenario, if you were asked to deliver a letter to John who is in Building 184 and on the 5th floor, how would you find John? You would have to go to each room and ask for John, and make sure it is the correct John before delivering the letter to him. This might take longer. This is roughly what happens in our scripts. As our descriptions get more and more generic, the time it takes to identify the object increases. Therefore, even though wildcard characters can simplify our work, we should be a little careful how we use them.

Regular-Expressions.Info is a good source to learn regular-expressions. We will now do the exact same test we did about with Banner2.gif, but this time using some more regex style characters.

' Using the first few characters of the title and the first few characters of the imageBrowser("title:=Welc\w+\D+\w+").Page("title:=Welc\w+\D+\w+").Image("file name:=ban\w+\d+\.\w+").Highlight ' Using the last few characters of the title with first and last characters of the imageBrowser("title:=\w+\D+\w+ours").Page("title:=\w+\D+\w+ours").Image("file name:=b\w+2\.gif").Highlight' Same as above for Browser and Page, but '...' for imageBrowser("title:=\w+\D+\w+ours").Page("title:=\w+\D+\w+ours").Image("file name:=b\w+2\....").Highlight' Same as above, but replaced 'b' with a '.'Browser("title:=\w+\D+\w+ours").Page("title:=\w+\D+\w+ours").Image("file name:=.\w+2\....").Highlight

In the proceeding article we will cover Ordinal Identifiers and also see how to create a simple test module for a login process

Descriptive Programming (DP) Concepts – 1 ;Introduction : Descriptive programming has become the technique of choice for many QTP test developers. We can talk about its advantages and disadvantages all day, but here, we’ll only discuss the concepts and come up with our own idea of what it does better, and what it doesn’t :). This is going to be a very quick refresher before we move on to its everyday application by completing an end-to-end testcase.

Page 85: Releavantcodes

The idea behind descriptive programming is for automation developers to instruct QTP which properties they would like to use to identify an object, instead of having QTP to choose them itself. If done correctly, this can help create robustness in scripts, ultimately requiring less maintenance-time and more development time. Let’s begin.

But wait, before we really begin, we must understand QTP’s Object Spy. It is an inbuilt tool that enlists all of the test-object and runtime-object properties. These properties are different for different types for objects. For example, an image has a property called ‘file name’ whereas a listbox doesn’t. Instead, a listbox has a special ‘all items’ property whereas the image doesn’t. This discussion will be limited to the usage test-object properties to identify objects. Below are 2

snapshots of the Object Spy: Object Spy Icon

Object Spy Window

Now, let’s open www.Google.com and use the object spy to retrieve all properties of the search box:

Page 86: Releavantcodes

Object Spy: WebEdit Properties : Notice the image above. The editbox has a HTML TAG property with its corresponding value ‘INPUT’. This means, the editbox takes some input from the user – which is true because we do set some value in it! It also has a ‘MAX LENGTH’ property, with a value of ‘2048′. This means, you can enter a maximum of 2048 characters in it (the best source to see all of the Test-Object properties of objects is the QTP help itself). Below you will see an editBox which can contain a maximum of 9 characters: Test maxLength:

You can really use all these properties to identify this editbox, but, do we really need to use all of them? No. That is the most important idea behind descriptive programming – we only use what we need. Below is how we write descriptions for objects:

ObjectClassName("property:=value", "property:=value") ' ofcourse we're not limited to only 2 properties. We can write more:ObjectClassName("property:=value", "property:=value", "property:=value")

Above, ObjectClassName (in Web applications) can be Browser, Page, Frame, WebEdit, Image etc. Properties come from the left column the ObjectSpy column whereas values are in the right column. We can include as many properties as we want, but in reality, we only need to add a few to uniquely identify the object. Knowing which properties should suffice to uniquely identify can object will come from experience and practice. Below is a description I created for this editbox (WebEdit):

'ObjectClassName( "property1:=value1", "property2:=value2" )WebEdit( "name:=q", "html tag:=INPUT" )

I already mentioned the HTML TAG and its value INPUT above. We’ve also added a new property/value here: ‘name:=q’. Is this enough to uniquely identify the object? Yes. But is it enough to make our script work? No, sadly its not.. and that is because, we haven’t yet created descriptions for its parent objects: Browser & Page. Below are the snapshots of the spied browser and page objects:

Page 87: Releavantcodes

Object Spy: Browser Properties

Object Spy: Page Properties

Browser description: 'ObjectClassName( "property1:=value1" ) Browser( "title:=Google" )

Page description Page( "title:=Google" )

Now, we will connect all these descriptions and form a hierarchical tree:

Browser("title:=Google").Page("title:=Google").WebEdit("name:=q","html tag:=INPUT")

You might wonder why I have omitted the WebTable below the Page and above the WebEdit object. In practice, we can also skip the Page object to identify the WebEdit. But, why did I skip the WebTable after all!? When you experiment more with DP, you will discover that some objects are embedded in many WebTables, and it will become cumbersome if we were to include all WebTables in the hierarchy to get to the object of interest (thanks to the person who thought that will be a terrible idea!). Example of the previously mentioned scenario:

Page 88: Releavantcodes

Object Spy: Multiple WebTables

To complete the statement above, we will add an event. In QTP, events can be described as actions on target objects. For example, a WebEdit has a ‘Set’ event. we use the ‘Set’ method of a WebEdit to set a value:

Browser("title:=Google").Page("title:=Google").WebEdit("name:=q","html tag:=INPUT").Set "DP"

Set is a QTP event to put in a value in the edit box. Different objects have different events. For example: an Image has a ‘Click’ event associated with it.

This, we did without using Object Repository. The same concept applies to all objects regardless of what your environment is. We perform actions on child objects by accessing their object hierarchies. Let’s complete the above example by searching for our keywords (use the spy again on the search button):

Browser("title:=Google").Page("title:=Google").WebEdit("name:=q", "html tag:=INPUT").Set "DP"Browser("title:=Google").Page("title:=Google").WebButton("name:=Google Search").Click

This is how the same code will look like if we had recorded this process:

Browser("Google").Page("Google").WebEdit("q").Set "DP is great"Browser("Google").Page("Google").WebButton("Google Search").Click

These properties are now stored in QTP’s Object Repository (OR). There is another way we can create object descriptions, which is done by setting a reference:

' Creating Browser description' "title:=Google"Set oGoogBrowser = Description.CreateoGoogBrowser( "title" ).value = "Google" 

Page 89: Releavantcodes

' Creating Page description' "title:=Google"Set oGoogPage = Description.CreateoGoogPage( "title" ).Value = "Google" '* Creating WebEdit description' "html tag:=INPUt", "name:=q"Set oGoogWebEdit = Description.CreateoGoogWebEdit( "html tag" ).Value = "INPUT"oGoogWebEdit( "name" ).Value = "q"

Once we do the above, we can use this descriptions in our script:

Browser(oGoogBrowser).Page(oGoogPage).WebEdit(oGoogWebEdit).Set "DP is great"

The only time I use this technique is to retrive object collections through ChildObjects (we will discuss this in the coming tutorials).

Let’s do another example. Again, we will use Google, but instead of setting a value, we will click an object. You can choose any Link on the page; I chose the link ‘Images’:

Object Spy: Google Images Link

'ClassName("property:=value").ClassName("propert1:=value").ClassName("property:=value").EventBrowser("title:=Google").Page("title:=Google").Link("innertext:=Images", "html tag:=A").Click

This time, instead of ‘Set’ we used ‘Click’. Following is a list of events we perform on Web objects:

Object Event

Image Click

Page 90: Releavantcodes

WebButton Click

WebCheckBox Set

WebEdit Set

WebElement Click

WebList Select

WebRadioGroup Select

QTP: Closing Multiple Browser Windows This article outlines a few techniques to close IE (and Firefox) browsers. ChildObjects & .Close MethodThis is probably the most common technique used. It uses a Browser Description Object and a loop to close all Browsers in the collection.

Dim oDesc, x 'Create a description objectSet oDesc = Description.CreateoDesc( "micclass" ).Value = "Browser" 'Loop through the collection and close each browserIf Desktop.ChildObjects(oDesc).Count > 0 Then For x = 0 to Desktop.ChildObjects(oDesc).Count - 1 Browser( "creationtime:=" & x ).Close NextEnd If

Similarly, we can modify the snippet above and close all browsers except Quality Center:

Dim oDesc, x'Create a description objectSet oDesc = Description.CreateoDesc( "micclass" ).Value = "Browser"'Close all browsers except Quality CenterIf Desktop.ChildObjects(oDesc).Count > 0 Then For x = 0 to Desktop.ChildObjects(oDesc).Count - 1 If InStr(1, colBrowser(i).GetRoProperty("Name"), "Mercury Quality Center") = 0 Then Browser( "creationtime:=" & x ).Close End If NextEnd If

SystemUtil MethodsSystemUtil has the following methods that can be used to close processes, including browsers:

.CloseProcessByName

SystemUtil.CloseProcessByName "iexplore.exe"

Page 91: Releavantcodes

.CloseProcessByHWND: Uses windows handle to close a window. Dim HWND: HWND = Browser( "title:=Google" ).GetROProperty( "HWND" )

SystemUtil.CloseProcessByHWND HWND

.CloseProcessByWndTitle: Uses window title to close it.

SystemUtil.CloseProcessByWndTitle "Google", True

TSKill with SystemUtil : SystemUtil.Run "tskill", "iexplore" Process ID with SystemUtil PID = Browser("title:=Google").GetROProperty("process id")

SystemUtil.CloseProcessByID PID

SystemUtil.CloseProcessByID was shared by Pragya in the following topic. Pragya, thanks for sharing this concept with us :)

WMI ; This technique shuts down the processes (as seen in the Task Manager) instead of closing the actual window using its handle. Close all IE Browsers using WMI:

strSQL = "Select * From Win32_Process Where Name = 'iexplore.exe'"Set oWMIService = GetObject("winmgmts:\\.\root\cimv2")Set ProcColl = oWMIService.ExecQuery(strSQL)For Each oElem in ProcColl oElem.TerminateNextSet oWMIService = Nothing

The WMI concept can be used further to close all IE as well as all Firefox browsers.

Close all IE & Firefox Browsers Using WMI:

strSQL = "Select * From Win32_Process Where Name = 'iexplore.exe' OR Name = 'firefox.exe'"Set oWMIService = GetObject("winmgmts:\\.\root\cimv2")Set ProcColl = oWMIService.ExecQuery(strSQL)For Each oElem in ProcColl oElem.TerminateNextSet oWMIService = Nothing

QTP: Retrieve The Count of Objects in a Browser Efficiently I have frequently read on forums about automation developers struggling to find the correct number of object count in their applications. The only trick at times is to isolate the number of hidden objects in the application with the number of non-hidden objects. The 2 methods below: GetObjectCount and GetVisibleObjectCount do exactly that. In short:

GetObjectCount: Retrieves the total count of objects on the webpage. This includes hidden + non-hidden objects.

GetVisibleObjectCount: This retrieves the number of non-hidden objects on the webpage.

Page 92: Releavantcodes

Usage

'This is where the objects are being searched for:Set BaseObject = Browser( "title:=Yahoo.*" ).Page( "micclass:=Page" ) ' Retrieve the total number of Link ObjectsMsgBox GetObjectCount( BaseObject, "Link" )' Retrieve the total non-hidden number of Link ObjectsMsgBox GetVisibleObjectCount( BaseObject, "Link" )

How its done:

Class clsClassCount  Public CountType  '————————————————————————————————————————————— ' Name: Function GetClassCount (Public) ' ' Purpose: ' ' Input: ' BaseObject- Object containing the ClassName objects ' ClassName- MicClass for which the count is being retrieved for ' ' Output: ' Integer ' ' Author: Anshoo Arora ' '————————————————————————————————————————————— Public Function GetClassCount( BaseObject, ClassName ) ' As Integer '————————————————————————————————————————————— BaseObject.Init If Not BaseObject.Exist( 0 ) Then Reporter.ReportEvent micWarning, "GetClassCount", "BaseObject was not found." GetClassCount = -1 Exit Function End If  Dim oDesc, intCount intCount = 0

Set oDesc = Description.Create oDesc( "micclass" ).Value = ClassName  intCount = BaseObject.ChildObjects( oDesc ).Count  If Not CountType = "ALL" Then oDesc( "x" ).Value = 0 intCount = intCount - BaseObject.ChildObjects( oDesc ).Count End If  GetClassCount = intCount End Function End Class Public refClassCount: Set refClassCount = New clsClassCount '————————————————————————————————————————————— ' Name: Function GetObjectCount (Public)

Page 93: Releavantcodes

' ' Purpose: Retrieve object count (visible + hidden)' ' Input:' BaseObject- Object containing the ClassName objects' ClassName- MicClass for which the count is being retrieved for' ' Output:' Integer'————————————————————————————————————————————— Public Function GetObjectCount( BaseObject, ClassName ) ' As Integer'————————————————————————————————————————————— refClassCount.CountType = "ALL" GetObjectCount = refClassCount.GetClassCount( BaseObject, ClassName )End Function '————————————————————————————————————————————— ' Name: Function GetVisibleObjectCount (Public)' ' Purpose: Retrieve visible objects count' ' Input:' BaseObject- Object containing the ClassName objects' ClassName- MicClass for which the count is being retrieved for' ' Output:' Integer'————————————————————————————————————————————— Public Function GetVisibleObjectCount( BaseObject, ClassName ) ' As Integer'————————————————————————————————————————————— refClassCount.CountType = "" GetVisibleObjectCount = refClassCount.GetClassCount( BaseObject, ClassName )End Function

Retrieve Server Response using XMLHTTP I recently received the following question in my mailbox yesterday: Is it possible to retrieve the text of a page without physically opening it?

Yes, it’s possible to retrieve the document text without physically opening the page. It can be done though an XMLHTTP Request. XMLHTTP is short for Extensible Markup Language HyperText Transfer Protocol. “The XMLHttpRequest object implements an interface exposed by a scripting engine that allows scripts to perform HTTP client functionality, such as submitting form data or loading data from a server.”1

In the snippet below, I’ve created a simple mechanism to retrieve server response from this blog. The ‘OPEN’ method is used to assign a method (GET, POST, HEAD) of the pending request. The ‘SEND’ method sends the request (as detailed in the Open method) to the server.

Dim oXMLHTTPSet oXMLHTTP = CreateObject( "Microsoft.XMLHttp" ) With oXMLHTTP .open "GET", "http://www.relevantcodes.blogspot.com", False .send  Reporter.ReportEvent micInfo, "Status", .statusText Reporter.ReportEvent micInfo, "Response", .responseTextEnd with

Page 94: Releavantcodes

 Set oXMLHTTP = Nothing

XMLHTTP Request Status

XMLHTTP Request Response

Search Web Object – Simplified : It can be quite confusing at times when an object exists on a page, but it is hidden or multiple instances of an object exists with some instances hidden. The IsObjectFound class implements the search function for the specified web object and returns ‘True’ if a visible instance of the object is found. Benefits of using this class:

The user does not have to specify the target browser to search for the object. Seperate code is not required to bypass hidden objects. IsObjectFound Class will

automatically work around the hidden objects and look only for the non-hidden/visible ones.

If multiple instances of the object exists, and the index identifier is not supplied in the code, the class will automatically find all the objects, but return ‘False’.

This class is created with performance in mind. The user will not have to create stand-alone code everytime. IsObjectFound can simply

be used. It creates the description object. Only the important properties must be supplied as

strings, or as an array.

The following code can be tested on www.Google.com:

Msgbox IsObjectFound( "WebEdit", "name:=q" ) ' will return TrueMsgbox IsObjectFound( "WebEdit", Array("name:=q", "index:=0") ) ' will return True MsgBox IsObjectFound( "Link", "innertext:=Images" ) ' will return TrueMsgbox IsObjectFound( "WebElement", Array("innertext:=Web", "index:=0")) ' will return TrueMsgbox IsObjectFound( "", "" ) ' will return False

Page 95: Releavantcodes

Msgbox IsObjectFound( "WebEdit", "" ) ' will return FalseMsgbox IsObjectFound( "", "name:=user" ) ' will return False

Creating Conditional style code with IsObjectFound:

If IsObjectFound("WebEdit", "name:=q") Then Msgbox "Object Found" ' Object Found

'micClass is not specified: Return 'False'If IsObjectFound( "", "name:=user" ) Then MsgBox "Object Found"Else MsgBox "Object not found"End If