RSS
 

Archive for the ‘Quick Test Professional (QTP)’ Category

#27: How to modify script in QTP debug mode

25 Nov

The only thing that I do not like in QTP is scripts are read only in debug mode: if an error pop up during a playback in QTP, and I choose to “Debug”, then I can not modify anything since the script is read only.

However, TestPartner is much more convenient in this particular area, since its scripts can be modified during debug mode: once you choose to debug, the playback is paused, and you can modify your script, and drag the current step indicator (i.e., the yellow arrow) to the line where you want the script to resume.

The only work around in QTP debug mode is to use the Command tab in the Debug Viewer:

Using the Debug Viewer
You use the Debug Viewer pane to view, set, or modify the current value of objects or variables in your function library, when it stops at a breakpoint, or when a step fails and you select the Debug option. The Debug Viewer is useful for debugging operations (functions) in a business component, but is not intended for use with other types of component steps.

To open the Debug Viewer pane:

Choose View > Debug Viewer. The Debug Viewer pane opens.

The Debug Viewer tabs are used to display the values of variables and objects in the main script of the selected subroutine.

Command Tab
Use the Command tab to execute a line of script in order to set or modify the current value of a variable or VBScript object in your function library. When the run continues, QuickTest uses the value that you set.

A detailed example about how to use the Debug Viewer and its Watch Tab, Variables Tab, and Command Tab can be viewed from here.

 

#26: Error handling in VBScript (QTP)

24 Nov

In post#13 and post#23, we talked about how to do error handling in Visual Basic of Application (VBA), which is the scripting language for TestPartner. In today’s post, we will discuss how to handle errors in VBScript, which is the scripting language for QTP.

In VBA, there are 3 error handling statements (for the meaning of these statements, please refer to post#13 or post#23.):

1, On Error GoTo line
This statement is NOT supported by VBScript, but you can use a subroutine to handle error output . See the script below.

Const adOpenDynamic = 2
Const adLockOptimistic = 3
Const adCmdTableDirect = 512

Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
Set errorObject = CreateObject("ADODB.Error")

On Error Resume Next
cn.Provider = "sas.localprovider.1"
cn.Properties("Data Source") = "c:\testdata"

cn.Open
rs.Open "lostDataset", cn, adOpenDynamic, adLockOptimistic, adCmdTableDirect
DisplayErrorInfo
rs.Close
cn.Close

sub DisplayErrorInfo()
  For Each errorObject In rs.ActiveConnection.Errors
    MsgBox "Description: " & errorObject.Description & Chr(10) & Chr(13) & _
           "Number:      " & Hex(errorObject.Number)
  Next
End Sub

2, On Error Resume Next
This statement is supported by VBScript.

3, On Error GoTo 0
This statement is supported by VBScript.

============================================

More reading material:
Error Handling in VBScript, Part One
Error Handling In VBScript, Part Two
Error Handling in VBScript, Part Three
To Err Is VBScript – Part 1
To Err Is VBScript – Part 2
Handling Error Objects

 

#25: Differences of Funtion that returns array in VBScript and VBA

17 Nov

To create and call a Function that returns array in VBScript and VBA are slightly different (QTP uses VBScript and TestPartner uses VBA):

In VBA,

Function GetExcelSheetData(FilePath As String, SheetName As String)
Dim CellArray() As String 'A dynamic array that is not sized in the Dim statement
... Calculate RowCount and ColumnCount
ReDim CellArray(RowCount, ColumnCount) As String
... Populate data in CellArray
GetExcelSheetData = CellArray()
End Function
Sub Main()
Dim SheetData() As String 'Dynamic array
.. Calculate RowCount and ColumnCount
ReDim SheetData(RowCount, ColumnCount) As String
SheetData() = Function_GetExcelSheetData.GetExcelSheetData(ExcelFilePath_MeasurementTab, "Package")

In VBScript,

Function Function_ImportExcelToArray(FilePath, SheetName)
Dim CellArray 'A dynamic array that is not sized in the Dim statement
... Calculate RowCount and ColumnCount
ReDim CellArray(RowCount, ColumnCount)
... Populate data in CellArray
Function_ImportExcelToArray = CellArray
'Note: if write "Function_ImportExcelToArray = CellArray()",
'then will get error "Subscript out of range: 'CellArray'".
End Function
Sub Sub_LoginRedirect()
Dim PageURL
'DO NOT write "PageURL()",
'otherwise, will get error "type mismatch".
.. Calculate RowCount and ColumnCount
ReDim PageURL(RowCount, ColumnCount)
PageURL = Function_ImportExcelToArray(InputExcel, "Sub_LoginRedirect")
'Note: if write "PageURL() = Function_ImportExcelToArray(InputExcel, "Sub_LoginRedirect")",
'then will get error "Subscript out of range: 'PageURL'".

Conclusion, in VBScript, do not use Array with empty brackets, such as Array(), but you can use not-empty brackets, such as Array(1, 2).

===========================================================

More information about Array in VBScript:

Much of the time, you only want to assign a single value to a variable you have declared. A variable containing a single value is a scalar variable. Other times, it is convenient to assign more than one related value to a single variable. Then you can create a variable that can contain a series of values. This is called an array variable. Array variables and scalar variables are declared in the same way, except that the declaration of an array variable uses parentheses ( ) following the variable name. In the following example, a single-dimension array containing 11 elements is declared:

Dim A(10)

Although the number shown in the parentheses is 10, all arrays in VBScript are zero-based, so this array actually contains 11 elements. In a zero-based array, the number of array elements is always the number shown in parentheses plus one. This kind of array is called a fixed-size array.

You assign data to each of the elements of the array using an index into the array. Beginning at zero and ending at 10, data can be assigned to the elements of an array as follows:

A(0) = 256
A(1) = 324
A(2) = 100
. . .
A(10) = 55

Similarly, the data can be retrieved from any element using an index into the particular array element you want. For example:

. . .
SomeVariable = A(8)
. . .

Arrays aren’t limited to a single dimension. You can have as many as 60 dimensions, although most people can’t comprehend more than three or four dimensions. You can declare multiple dimensions by separating an array’s size numbers in the parentheses with commas. In the following example, the MyTable variable is a two-dimensional array consisting of 6 rows and 11 columns:

Dim MyTable(5, 10)

In a two-dimensional array, the first number is always the number of rows; the second number is the number of columns.

You can also declare an array whose size changes during the time your script is running. This is called a dynamic array. The array is initially declared within a procedure using either the Dim statement or using the ReDim statement. However, for a dynamic array, no size or number of dimensions is placed inside the parentheses. For example:

Dim MyArray()
ReDim AnotherArray()

To use a dynamic array, you must subsequently use ReDim to determine the number of dimensions and the size of each dimension. In the following example, ReDim sets the initial size of the dynamic array to 25. A subsequent ReDim statement resizes the array to 30, but uses the Preserve keyword to preserve the contents of the array as the resizing takes place.

ReDim MyArray(25)
. . .
ReDim Preserve MyArray(30)

There is no limit to the number of times you can resize a dynamic array, although if you make an array smaller, you lose the data in the eliminated elements.

 

#22: Decrease the disk space used by QTP

23 Oct

I am developing a set of automated test scripts for the YouPlayOff webset in QTP, and notice that the script files will take a lot of (may be too much) disk space eventually. I reviewed the default test folder structure in QTP, and found the following problems:

1, Script files in the Test folder can be big:
- An empty Object Repository file takes 192K space (if not empty, will take more space). Each Test folder contains at least 2 actions, i.e., Action0 and Action1, and each action has 1 Object Repository file. As a result, an empty Test folder costs 192K*2=384K space just because the Object Repositories. If there are more actions in the Test folder, the Object repository files will take more space.
- The Excel file, i.e., the Data Table, also takes too much space. Each action use one Excel sheet, and the actions that were called by the main action also has their own Excel sheets. In a complex test, Excel files may take up to 4MB space.

2, Script files in the Test folder are too many:
A simplest QTP test script contains 4 folders, 15 files, and the number of files increases as the number of Action increases.

Due to the above 2 problems, a finished QTP project may take 700-800MB space, and contain thousands of script files. This will make the scripts difficult to maintain and to do the version control, and if we want to transfer the scripts to other computers, it will take a long time.

However, if instead of
- Reuse actions (Action is saved in a folder contains multiple files)
- Use Local Object Repository for each action
- Use Local Excel Sheet (i.e., Data Table) for each action
We
- Reuse functions (Function is a single file)
- Use Shared Object Repository for multiple scripts (Reduce the number of Object Repository files)
- Use Shared Excel File with multiple sheets (Reduce the number of Excel files)
then the number of script files in the QTP project will decrease dramatically, and the size of the QTP project can be decreased to 10MB.

Now, I will explain how to do this in detail:
First, record and modify your script, then save it as “Action xyz”. Open the folder of “Actions xyz”, you will find an Excel file – “Default.xls” for the Data Table information, and there are 2 sub-folders: i.e., Action0 and Action1. Action0 is a default action created by QTP, and Action1 is the real action that created by you. if you are interested in understanding more about the difference between Action0 and Action1, please click here. Within the folder of Action1, you will see a file called as “ObjectRepository.bdb” which is the local ObjectRepository for this Action. Blow is an indication of the layout of the “Action xyz” folder.

  • folder “Action xyz”

    • folder Action0
    • folder Action1
      • ObjectRepository.bdb
      • other stuff
    • Default.xls
    • other stuff

 

Second, in QTP, choose File-New-Function Library, copy the code from “Action xyz” and paste it to this new Function Library, modify it to either Sub or Function. For how to write a Sub and Function, please see here. Save the Function Library as “Sub xyz” or “Function xyz”. Save them as *.qfl, *.vbs, or *.txt doesn’t matter, they will behave the same. Open the folder where “Sub xyz” or “Function xyz” is saved, you will find out, compare to “Action xyz” (which is a folder with multiple files and sub-folders), “Sub xyz” or “Function xyz” is just a single file!

Third, create a driver Action for your project. This action will be the only action for your project, and it will function as the entry point of your project. All the other scripts are saved as either Subs or Functions in the project, and these Subs and Functions will be executed by the driver Action. The driver Action is a normal Action, and it has same layout as “Action xyz”, i.e., 2 sub-folders and some files, but since all the other scripts are saved as single files (*.qfl, *.vbs, or *.txt), so the total disk space used will be decreased a lot! Now you know, one project actually only needs one Action folder! This is probably why we can only open a single Action in QTP.

The script of the Sub, Function, and driver Action may look like this:

“Function xyz.qfl”:

   Function function_xyz(inputs)
      some statements
      function_xyz = some value
      some statements
   End Function

“Sub xyz.qfl”:

   Sub sub_xyz()
      some statements
      Call function_xyz(inputs)
      some statements
   End Sub

“driver Action”:

   Call sub_xyz()

Forth, associate the Subs and Functions with the driver Action (open Subs, Functions and the driver Action in QTP, right click inside the editing area of the Sub or Function, click the last item in the pop-up menu to associate with the driver Action). Note: if the Subs and Functions are not in the same folder of the driver Action, or the Subs and Functions are in different folders, you should still be able to associate them with the driver Action.

Fifth, set up the Object Repository for the driver Action. Currently, the “ObjectRepository.bdb” file within the Action1 folder in the driver Action folder is 192Kb, i.e., it is empty. Since the Object Repository is empty, the driver Action can not be run. We can use existing Object Repositories from recorded actions to create Object Repository for the driver Action. For example, we recorded ActionX, ActionY, and ActionZ, and have transferred them to SubX, FunctionY, and SubZ. Say the Object Repositories for ActionX, ActionY, and ActionZ are “ObjectRepositoryX.bdb”, “ObjectRepositoryY.bdb” and “ObjectRepositoryZ.bdb”, respectively.

  • step1, open ActionX, and choose Resources-Object Repository. From the Object Repository window, choose File-Export Local objects. Export it to “ObjectRepositoryX.tsr”. *.bdb is local Object Repository file, and *.tsr is shared Object Repository file.
  • step2, repeat step1 for “ObjectRepositoryY.bdb” and “ObjectRepositoryZ.bdb”, export them to “ObjectRepositoryY.tsr” and “ObjectRepositoryZ.tsr”, respectively.
  • step3, open the driver Action, and choose Resources-Object Repository Manager. From the Object Repository Manager window, choose Tools-Object Repository Merge Tool. In the Object Repository – Merge Tool window, choose to merge “ObjectRepositoryX.tsr” and “ObjectRepositoryY.tsr”, and save the merge result as “ObjectRepositoryXY.tsr”.
  • step4, repeat step3 for “ObjectRepositoryXY.bdb” and “ObjectRepositoryZ.bdb”. Save the merge result as “ObjectRepositoryXYZ.tsr”.
  • step5, move “ObjectRepositoryXYZ.tsr” into the Action1 folder of the driver Action folder, and delete “ObjectRepository.bdb” inside the folder, and rename “ObjectRepositoryXYZ.tsr” as “ObjectRepository.bdb”.

 

Now, the driver Action can be run properly.

Sixth, combine “Default.xls” files from different recorded actions into one excel file (multiple sheets), and save it under the driver Action’s folder. Modify the scripts of Subs and Functions to cope with the excel file. If you want to import data from the excel file to an array, please see post#9. If you want to export data from array to an excel sheet, please refer to post#16.

Seventh, if there are checkpoints in the recorded Actions, you need to transfer them into normal VBScript, since QTP checkpoints won’t work for Subs nor Functions, i.e., checkpoints created by QTP won’t be recognized by *.qfl, *.vbs, nor *.txt file.

=========================================================

You can download an example folder structure and code by clicking here. In the “Tests_YouPlayOff” folder, the “YouPlayOff” folder is the driver action folder, which you can loaded in QTP as a test. In the Action1, there are only two lines of code:

Call Sub_LoginNonconfirmed()
Call Sub_LoginRedirect()

The “Sub_LoginNonconfirmed()” is saved as a *.qfl file, which can be opened from notepad, or opened in QTP as a function library. This “Sub_LoginNonconfirmed.qfl” is a substitution of the test/action mentioned in post#6, and in this sub, “Sub_Login.qfl” is called.

The “Sub_LoginRedirect()” is also saved as *.qfl file, and it is a substitution of the test/action mentioned in post#5. In the “Sub_LoginNonconfirmed.qfl” file, “Sub_Login.qfl” and 3 functions are called. The 3 functions are “Function_ExcelColumnCount.qfl”, “Function_ExcelRowCount.qfl”, and “Function_ExportArrayToExcel.qfl”, and they are used to import data from Excel “Input.xlsx” to the sub. The Excel is a substitution of the Data Table, and the location of the Excel is defined in “Constant.qfl” which is used in “Sub_LoginNonconfirmed.qfl”.

 

#16: Export data from Two-Dimensional Array (2D array) to an Excel Sheet

25 Jul

In Post#9, we talked about how to import data from Excel (with multiple sheets) to an Array.

In this post, we will discuss that how to export data from a Two-Dimensional Array (2D array) to an Excel sheet.

Function SaveArrayAsExcelSheet(ArrayData() As String, ArrayRowCount As Integer, ArrayColumnCount As Integer, ExcelFilePath As String, SheetName As String)

'On error jump to "Error Handler:" line
On Error GoTo ErrorHandler

'Variable declaration
Dim SheetCount As Integer

'Open the Excel file
Set ObjExcel = CreateObject("Excel.Application")
Set ObjWorkBook = ObjExcel.Workbooks.Open(ExcelFilePath)

'Count how many sheets in the Excel file
SheetCount = ObjWorkBook.WorkSheets.Count

'If the sheet named "SheetName" exists, delete it
For i = 1 To SheetCount
    If ObjWorkBook.WorkSheets(i).Name = SheetName Then
        ObjExcel.DisplayAlerts = False  'Disable the delete confirmation message
        ObjWorkBook.WorkSheets(i).Delete
        ObjExcel.DisplayAlerts = True   'Enable the delte confirmation message
        Exit For
    End If
Next

'Create a new sheet in the Excel file and name this sheet as "SheetName"
ObjWorkBook.WorkSheets.Add.Name = SheetName

'Transfer data from an array to a Excel sheet
Dim ObjRange As Excel.Range
For i = 1 To SheetCount
    If ObjWorkBook.WorkSheets(i).Name = SheetName Then
        Set ObjRange = ObjWorkBook.WorkSheets(i).Range(ObjWorkBook.WorkSheets(i).Cells(1, 1), ObjWorkBook.WorkSheets(i).Cells(ArrayRowCount, ArrayColumnCount))
        ObjRange.Value = ArrayData()
    End If
Next

'Save and Close the Excel file
ObjWorkBook.Save
ObjWorkBook.Close

Exit Function

ErrorHandler: ' This is a normal VBA line label
    MsgBox Err.Description
    ObjWorkBook.Close

End Function

The VBA script above solves the problem (it will add or overwrite a sheet in the Excel file to receive data). The script should be saved as an shared module in TestPartner. To make the script work, you need to go to Tools->Reference to check the reference for Microsoft Excel, otherwise, TestPartner will give you an error on line “Dim ObjRange As Excel.Range”. (see Chapter 8 page 96 of “TestPartner Advanced Training Guide.pdf for Test Partner 5.2” listed in post#8 for more details.)

If you want to apply the script in QTP which uses VBScript instead of VBA, you need to make some minor modifications:

A, The function script needs to be created in the QTP Function Library (File – New – Function Library);

B, In the function script, delete anything related to the ErrorHandler (2 paragraphs);

C, In the function script, delete any definition for variable type, e.g., modify “SheetName As String” to “SheetName”, and change “Dim SheetCount As Integer” to “Dim SheetCount”.

D, Create a Test/Action to execute the function script, and the test/action will look like this (compare to TestPartner test script, no “Sub” nor “End Sub”, no need to assign the function to a variable, and no function file name nor brackets, i.e.,
in Testpartner (In TestPartner, if the function does not have a return value, but it has more than 1 input values, then in order to execute the function, you still need to define a variable in your test script, and assign the function to the variable.):

Sub Main()
a = Function_SaveArrayAsExcelSheet.SaveArrayAsExcelSheet(ArrayName, 15, 60, "C:\...\MeasurementTab.xlsx", "Sheet2")
End Sub

in QTP:

SaveArrayAsExcelSheet ArrayName, 15, 60, "C:\...\MeasurementTab.xlsx", "Sheet2")

E, Associate the Function file with the Test/Action file (right click on the Function file script and choose “Associate…” on the pop up menu), then you can run the function by running the test/action.

The script above should be fast enough to handle 10,000 data, but if your data is more than 50,000, then you may experience some slowness (depends on your computer speed too). There are two ways to solve the speed problem:
1, export the data from array to csv, then convert csv to Excel.
2, use ExcelCreator.NET instead of Excel Object, which is more efficient.
For more details, please refer to this website (you need to understand Chinese) .

 

#11: Who can test Windows TreeView with Checkbox? QTP or TestPartner?

23 Jun


TreeView is a Windows Standard Class, and many Windows Applications use TreeViews. Such as the Workstation I was testing in post#10, the measurement Protocols belong to each measurement Package are listed in a TreeView Window as nodes. Each Portocol Node also has a CheckBox beside it, and I need to test if these CheckBoxes are checked or not.

In TestPartner/QTP, TreeView window is identified as the “TTreeView”/”WinTreeView” object:
1, The nodes inside the TreeView window can NOT be identified by the identification tool;
2, The TreeView window is identified as a TestPartner/QTP TreeView object, not a Windows Standard TreeView object, which means you can ONLY use the TestPartner/QTP built-in methods and properties for the “TTreeView”/”WinTreeView” object. You can NOT use the Windows Standard TreeView methods nor properties.

TestPartner/QTP do have some built-in methods and properties for the “TTreeView”/”WinTreeView” object to allow you to get access to or fetch the properties of the nodes inside the TreeView window. For example, in TestPartner, the most useful method is the “SelectItem” method. However, it disappointingly returns a Boolean…Actually, NONE of the TestPartner built-in “TTreeView” methods or properties can help you to get the properties of the CheckBoxes beside the TreeView nodes. For this particular feature, QTP is much more powerful. It has a built-in method called “CheckItemProperty” for the “WinTreeView” object, which can be used to check all kinds of properties of a TreeView node, including if the CheckBox is checked or not.

The way to use “CheckItemProperty” method in QTP is:
object.CheckItemProperty (Item, PropertyName, PropertyValue, [TimeOut])
One of its “PropertyName” (a String value) is “state”: indicates whether the tree-view control node has a check box, and whether it is selected, and its possible values:
0–the tree-view control node does not have a check box
1–the tree-view control node’s check box is not selected
2–the tree-view control node’s check box is selected
The script to test the Ultrasound workstation will look like this:

Window("the workstation").WinTreeView("SysTreeView32").CheckItemProperty "Measurements;Protocol name", "State", 2

Now we know that we can use QTP to easily check if a CheckBox besides a TreeView node is checked or not. However, how do we handle this issue with TestPartner? (The built-in “TTreeView” methods and properties in TestPartner are insufficient for us to test the TreeView CheckBox).

Here I provide two solutions to use TestPartner to test the TreeView CheckBox:
A, Write a separate script to identify the TreeView object in any Windows Applications as a Windows Standard TreeView Object. In this way, you will be able to use the standard TreeView methods and properties. For example, you can use the “SelectedImageIndex” Property to get the image list index value of the image that is displayed when a tree node is selected (images can be “no CheckBox”, “with CheckBox but not checked”, “with CheckBox and checked”).

This kind of script will be difficult to write. 8thString has wrote a script about how to get TreeView item information using Win32 api. The script is very long, if you are interested, you may want to have a look of it. Besides it is long, it is also very backended. The script talks “Process”, “Memory” and “kernel32″ a lot, which is actually a developer level’s code.

If you do not understand the above script from 8thString, you may want to read my second suggestion to solve the TreeView CheckBox issue with TestPartner:
B, Use the “BitmapExists” method to verify the existence of a pixel from the check mark (saved as a bitmap image called as “MeasurementTreeViewImage”) in the CheckBox area of the TreeView window. The script to test the Ultrasound workstation will look like the following:

Window("the workstation'").Attach
    Set obj = TreeView("Parent.Caption=Preferences")
    If obj.SelectItem("\Measurements\Protocol name") Then
        y = obj.MouseY
        obj.Scroll (tpScrollHorizontal)
        For x = 0 to obj.Width 'you can reduce the search area from the TreeView area to the ChceckBox area by hard coding like this: For x = 40 To 51
            If obj.BitmapExists("MeasurementTreeViewImage", Left:=x, Top:=y, Width:=1, Height:=1) Then
                UserCheck "mycheck", True, "'Protocol CheckBox is checked"
                Exit For
            End If
        Next
    End If
 

#9: Get data from Excel for TestPartner and QTP

11 Jun

QTP and TestPartner provide integrated features for you to test multiple data in one test case. In QTP, the feature is called Data Table, and in TestPartner it is called Active Data. However, both of the features have an disadvantage, which is they only support Data Table or Active Data that has a single sheet.

For example, in TestPartner, to make the Active Data working, first, you need to import an excel file to the Active Data asset. If your excel file has multiple data sheets, and you want to use each of them, this becomes impossible, since in the Active Data asset, you can only select one data sheet (done through the option tab). If you really want to use another data sheet, then you have to rename your excel file, and import it again, and this time, select another data sheet in the option tab.

Someone like me, who wants to save space on the Database and make data easy to maintain, would like to use one Excel file that has multiple sheets instead of use multiple Excel files contain only one sheet per file.

How to do this, we should really get ride of using the Active Data from TestPartner or the Data Table from QTP. We can get data directly from an external Excel file (contains single or multiple work sheets). I have written a function for this, and will show it to you below.

In TestPartner, you may want to save the following code in the Shared Module, since this script can be used across different projects:

Function GetExcelSheetData(FilePath As String, SheetName As String)

'On error jump to "Error Handler:" line
On Error GoTo ErrorHandler

'Variable declaration
Dim SheetCount As Integer
Dim RowCount As Integer
Dim ColumnCount As Integer
Dim CellArray() As String 'A dynamic array that is not sized in the Dim statement

'Open the Excel file
Set ObjExcel = CreateObject("Excel.Application")
Set ObjWorkBook = ObjExcel.Workbooks.Open(FilePath)

'Count how many sheets in the Excel file
SheetCount = ObjWorkBook.WorkSheets.Count

'Store the data from a specific worksheet into an array
For i = 1 To SheetCount
    If ObjWorkBook.WorkSheets(i).Name = SheetName Then

        'Count how many rows and columns in the Excel file (only for the used range)
        RowCount = ObjWorkBook.WorkSheets(i).UsedRange.Rows.Count
        ColumnCount = ObjWorkBook.WorkSheets(i).UsedRange.Columns.Count

        'Array is sized with the ReDim statement
        'after RowCount and ColumnCount are given values.
        ReDim CellArray(1 To RowCount, 1 To ColumnCount) As String

        'Store the data from the specific worksheet into CellArray
        For j = 1 To RowCount
            For k = 1 To ColumnCount
               CellArray(j, k) = ObjWorkBook.WorkSheets(i).UsedRange.Cells(j, k)
            Next
        Next

    End If
Next

'Close the Excel file
ObjWorkBook.Close

'Return CellArray
GetExcelSheetData = CellArray()

Exit Function

ErrorHandler: ' This is a normal VBA line label
    MsgBox Err.Description
    ObjWorkBook.Close

End Function

From the above script, you learned the following:
1, How to write a function with input parameters
2, When deal with external files, you better write something to handle errors
3, How to use “On Error GoTo” (more details see post#13)
4, How to define an array dynamically (Dim and Redim)
5, How to use Excel Object Model, including Excel.Application, Excel.Workbook, Excel. Worksheet, and Excel.Range. (see here)
6, How to return a value in a function

Make some modifications for the Function_GetExcelSheetData Function above, you should be able to write another 2 functions: one is called as Function_ExcelSheetRowCount, and the other one is called as Function_ExcelSheetColumnCount. These 2 functions will return how many rows or columns in a specific Excel sheet, and they will be used together with Function_GetExcelSheetData in my next post.

In order to test the above function, you can write a separate test script to execute it (“Function_GetExcelSheetData” is the file name):

Sub Main()
Dim A
A = Function_GetExcelSheetData.GetExcelSheetData("C:\...\MeasurementTab.xlsx", "Sheet1")
End Sub

In TestPartner, if the function does not have a return value, but it has more than 1 input values, then in order to execute the function, you still need to define a variable in your test script, and assign the function to the variable. For example, you have a function “ActiveExcelSheet(FilePath As String, SheetName As String)”, which has no return value, just to active a specific sheet in the Excel file. To execute the function, your script needs to look like this:

Sub Main()
Dim A
A = Function_ActiveExcelSheet.ActiveExcelSheet("C:\...\MeasurementTab.xlsx", "Sheet1")
MsgBox (A) 'this tells you A is nothing
End Sub

If the test script looks like this:

Sub Main()
Function_ActiveExcelSheet.ActiveExcelSheet("C:\...\MeasurementTab.xlsx", "Sheet1")
End Sub

TestPartner will inform you there is a “Syntax error” and won’t allow you to run the script. However, if the function has only one input variable and no return value, then you can execute it without assign it to a variable.

Now, let’s talk about how to take the above function (e.g., the “ActiveExcelSheet” function) to QTP. Since TestPartner uses VBA, but QTP uses VBScript, you need to make the following changes in the function script:
A, The function script needs to be created in the QTP Function Library (File – New – Function Library);
B, In the function script, delete anything related to the ErrorHandler (2 paragraphs);
C, In the function script, delete any definition for variable type, e.g., modify “(FilePath As String, SheetName As String)” to “(FilePath, SheetName)”, and change “Dim SheetCount As Integer” to “Dim SheetCount”.
D, Create a Test/Action to execute the function script, and the test/action will look like this (compare to TestPartner test script, no “Sub” nor “End Sub”, no need to assign the function to a variable, and no function file name nor brackets, i.e., “Function_ActiveExcelSheet.ActiveExcelSheet(…)” changes to “ActiveExcelSheet …”):

ActiveExcelSheet "C:\...\MeasurementTab.xlsx", "Sheet1"

E, Associate the Function file with the Test/Action file (right click on the Function file script and choose “Associate…” on the pop up menu), then you can run the function by running the test/action.

From this post, you may have a better understanding of QTP and TestPartner… Beside the object identifier, all the other features that you need for automation testing actually can be achieved by writing VB scripts in notepad, and save them as .vbs files…

Ok, these are just theoretically speaking, we will still write VB scripts in QTP and TestPartner instead of in notepad, right? Just for the convenience.

 

#7: Relationship among VBScript, Visual Basic for Applications (VBA), Visual Basic (VB), Visual Basic .NET, and JavaScript

28 May

QTP uses the Visual Basic Scripting Edition (VBScript) scripting language, TestPartner is based on the Visual Basic for Applications (VBA), and RFT’s test script is produced as either a Java or Visual Basic.net application.

VBScript is a subset of the Visual Basic Programming language. The result of the slimming down process is a very small language that is easy to use. JavaScript can do all that VBScript can do and far more. So why use VBScript instead of JavaScript? Because VBScript is much easier to learn than JavaScript.

Visual Basic for Applications (VBA) is another subset of the Visual Basic Programming language for use with Microsoft Word, Excel, Access etc. While it contains a good many features not supported by VBScript, the basic syntax, or construction of the language, is very similar.

RFT is not the main focus for this blog, at least not yet, so for Java vs. VB.NET, you only need to know that Java is programming environment controlled by Sun, and VB.NET is developed by Microsoft.

As said above, both VBScript and VBA are subsets of Visual Basic (VB). The final release of VB was version 6 in 1998. Microsoft’s extended support ended in March 2008 and the designated successor was Visual Basic .NET (now known simply as Visual Basic)

After learning the history of the VB family and JavaScript, let us play a small game by writing the VBScript in a notepad. This is very useful if you do not want to bother to repeat the same thing over and over again.

Copy the following code into a notepad, and save it as kk.vbs, then double click on it, and see what it will do.

Dim fs, file, TSTnumber
set fs = createobject( "Scripting.FileSystemObject" )
set file = fs.OpenTextFile( "RF.BAT", 2, true )

For TSTnumber=613 to 616
file.write "VsiColorModeRAW.exe tst-18-0"
file.write TSTnumber
file.write "\tst-18-0"
file.write TSTnumber
file.write ".raw.bmode tst-18-0"
file.write TSTnumber
file.write "\tst-18-0"
file.write TSTnumber
file.write ".raw.color tst-18-0"
file.write TSTnumber
file.write "\tst-18-0"
file.write TSTnumber
file.write ".raw.xml tst-18-0"
file.write TSTnumber
file.writeline "\Color"
Next
file.close
set fs=nothing
 

#6: Use QTP to test miscellaneous log in functions for YouPlayOff.com

25 May

In this blog, I will show you how to use QTP to test YouPlayOff.com for the following functions:
1, log in as a confirmed user, the “Add New” player link exists
2, Log in as non-confirmed user, the “Add New” player link won’t exists
3, Log in from log out page, the page will c to Home page

From this blog, you should be able to practice and learn the following:
1, How to define an object (i.e., not a number, not a string, etc.) (Dim and Set);
2, How to check if an object exists (object.exist);
3, How to write test result directly to the test result summary (Reporter.ReportEvent);
4, How to use If Then;

Dim addLink, addLinkText
addLinkText = "Add New"

'Confirmed user: addLink exists
Browser("YouPlayoff - Create and").Page("YouPlayoff - Create and").Sync
Browser("YouPlayoff - Create and").Navigate "http://testing.youplayoff.com/category/28/Hockey/?type=1"
Browser("YouPlayoff - Create and").Page("YouPlayoff - Hockey Players_2").Link("Sign In").Click
RunAction "Log in [Log in]", oneIteration

Set addLink = Browser("YouPlayoff - Create and").Page("YouPlayoff - Hockey Players").Link("text:=" & addLinkText)
If addLink.Exist Then
 Reporter.ReportEvent micPass, "Find the AddNew Link when logged in as confirmed user", "PASS"
Else
 Reporter.ReportEvent micFail, "Can NOT find the AddNew Link when logged in as confirmed user", "FAIL"
End If

'Check redirect function of login from logout page
Browser("YouPlayoff - Create and").Page("YouPlayoff - Hockey Players_3").Link("Sign out").Click
Browser("YouPlayoff - Create and").Page("Logout - YouPlayoff").Link("Sign In").Click
Browser("YouPlayoff - Create and").Page("Login - YouPlayoff").WebEdit("name").Set "guidebillion"
Browser("YouPlayoff - Create and").Page("Login - YouPlayoff").WebEdit("password").SetSecure "4c09bae5a2cf4dc2f458b8815ed6e83fdd06dd004f8f346d"
Browser("YouPlayoff - Create and").Page("Login - YouPlayoff").WebButton("Submit").Click
Browser("YouPlayoff - Create and").WinEdit("Edit").Check CheckPoint("Edit")

'Non-confirmed user: addLink does not exist
Browser("YouPlayoff - Create and").Navigate "http://testing.youplayoff.com/category/28/Hockey/?type=1"

Set addLink = Browser("YouPlayoff - Create and").Page("YouPlayoff - Hockey Players").Link("text:=" & addLinkText)
If addLink.Exist Then
 Reporter.ReportEvent micFail, "Find the AddNew Link when logged in as NON-confirmed user", "FAIL"
Else
  Reporter.ReportEvent micPass, "Can NOT find the AddNew Link when logged in as NON-confirmed user", "PASS"
End If

Browser("YouPlayoff - Create and").Page("YouPlayoff - Hockey Players").Link("Sign out").Click
Browser("YouPlayoff - Create and").Page("Logout - YouPlayoff").Link("Home").Click
 

#5: Use QTP to test Log in page redirect function for YouPlayOff.com

17 May

In this blog, let’s test the log in redirect function, i.e., from page A, choose log in, then the borrower should be redirected to page A.

This test is complicated, since I want to test multiple starting pages in one action. I would suggest you to try this first by yourself, and I am pretty sure you will have questions, then you can come back to read this blog and solve your questions one by one.

What scripting skills you can practice or learn from this test:
1, Call an existing action (RunAction);
2, Use the DataTable to test multiple inputs;
3, Difference between Global DataTable and Local DataTable (run multiple iterations);
4, How to define variables (Dim);
5, How to wait until certain page is loaded (Sync);
6, How to navigate to a web-page (Navigate);
7, How to handle URLs which contain question mark (?);
8, How to find certain character is in the string (InStr);
9, How to replace characters in a string (Replace);
10, How to use message box (msgBox);
11, How to identify an object from different pages dynamically (Page(“url:=” & PageURL).Link(“text:=” & SignInText));
12, How to use Select Case;
13, How to add comment in the Expert View (‘);
14, How to add a Checkpoint;
15, How to check according to the value from a DataTabel;
16, In order to start next iteration successfully, you need to reset the test environment at the end of the test.

'Page redirect fuction after log in
Dim PageURL
Dim SignInText
Dim SignOutText
PageURL = DataTable("A", dtGlobalSheet)
SignInText = "Sign In"
SignOutText = "Sign Out"

'Wait till the home page is open, this may take a while
Browser("YouPlayoff - Create and").Page("YouPlayoff - Create and").Sync

'Navigate to the URLs listed in the Data Table
Browser("YouPlayoff - Create and").Navigate PageURL

'When URL contains question mark needs special handle
'If InStr (PageURL, "?") <> 0 Then
 PageURL = Replace (PageURL, "?", "\?")
'End If
'msgBox (PageURL)

'From the page opened above, click the "Sign In" link
Browser("YouPlayoff - Create and").Page("url:=" & PageURL).Link("text:=" & SignInText).Click

'Call an existing action for log in only.
RunAction "Log in [Log in]", oneIteration

Select Case PageURL

'In the VBScript, Page("url:=" & "http://testing.youplayoff.com") can not be written as Page("url:=" & "http://testing.youplayoff.com/")
'As a result, in the data table, the data has to be stored as "http://testing.youplayoff.com" instead of "http://testing.youplayoff.com/"
'However, the real URL is "http://testing.youplayoff.com/" instead of "http://testing.youplayoff.com", so this case can not be checked by using the data table.
'After checking, log off
Case  "http://testing.youplayoff.com"
 Browser("YouPlayoff - Create and").WinEdit("Edit").Check CheckPoint("redirecttohomepage")
 Browser("YouPlayoff - Create and").Page("url:=http://testing.youplayoff.com").Link("text:=" & SignOutText).Click

'Login from register page, the home page will open
'After checking, log off
Case  "http://testing.youplayoff.com/register"
   Browser("YouPlayoff - Create and").WinEdit("Edit").Check CheckPoint("redirecttohomepage")
   Browser("YouPlayoff - Create and").Page("url:=http://testing.youplayoff.com").Link("text:=" & SignOutText).Click

'Login fromlogin page, the home page will open
'After checking, log off
Case  "http://testing.youplayoff.com/Login"
   Browser("YouPlayoff - Create and").WinEdit("Edit").Check CheckPoint("redirecttohomepage")
   Browser("YouPlayoff - Create and").Page("url:=http://testing.youplayoff.com").Link("text:=" & SignOutText).Click

'Login from logoutpage is tested in another test called "Log in as nonconfirmed user, redirect from logout page"

'Login from create page, the select-category  page will open (select-category is first step of create a playoff, but this step is only visible to logged in user)
'After checking, log off
Case  "http://testing.youplayoff.com/create"
 Browser("YouPlayoff - Create and").WinEdit("Edit").Check CheckPoint("RedirectAfterCreate")
 Browser("YouPlayoff - Create and").Page("url:=http://testing.youplayoff.com/create/playoff/select-category").Link("text:=" & SignOutText).Click

'Redirect to previous page after login, so use the data table to check
'After checking, log off
Case Else
 Browser("YouPlayoff - Create and").WinEdit("Edit").Check CheckPoint("RedirectToPreviousPage")
 Browser("YouPlayoff - Create and").Page("url:=" & PageURL).Link("text:=" & SignOutText).Click

End Select 

'Navigate back to home page
Browser("YouPlayoff - Create and").Navigate "http://testing.youplayoff.com/"

Global Data Table: (you can also read the data from an Excel file directly, see post#9)

http://testing.youplayoff.com

http://testing.youplayoff.com/register

http://testing.youplayoff.com/Login

http://testing.youplayoff.com/create

http://testing.youplayoff.com/categories

http://testing.youplayoff.com/categories/tree

http://testing.youplayoff.com/categories/popular

http://testing.youplayoff.com/playoffs

http://testing.youplayoff.com/recommended

http://testing.youplayoff.com/recent

http://testing.youplayoff.com/groups

http://testing.youplayoff.com/whatsup

http://testing.youplayoff.com/whatsup?show=2

http://testing.youplayoff.com/whatsup?show=3

http://testing.youplayoff.com/help

http://testing.youplayoff.com/about

http://testing.youplayoff.com/privacy

http://testing.youplayoff.com/terms

http://testing.youplayoff.com/contact