In this article by Meir Bar-Tal and Jonathon Lee Wright, authors of Advanced UFT 12 for Test Engineers Cookbook, we will cover the following recipes:
Implementing a class
Implementing a simple search class
Implementing a generic Login class
Implementing function pointers
(For more resources related to this topic, see here.)
Introduction
This article describes how to use classes in VBScript, along with some very useful and illustrative implementation examples. Classes are a fundamental feature of object-oriented programming languages such as C++, C#, and Java. Classes enable us to encapsulate data fields with the methods and properties that process them, in contrast to global variables and functions scattered in function libraries. UFT already uses classes, such as with reserved objects, and Test Objects are also instances of classes. Although elementary object-oriented features such as inheritance and polymorphism are not supported by VBScript, using classes can be an excellent choice to make your code more structured, better organized, and more efficient and reusable.
Implementing a class
In this recipe, you will learn the following:
The basic concepts and the syntax required by VBScript to implement a class
The different components of a class and interoperation
How to implement a type of generic constructor function for VBScript classes
How to use a class during runtime
Getting ready
From the File menu, navigate to New | Function Library…, or use the Alt + Shift + N shortcut. Name the new function library cls.MyFirstClass.vbs and associate it with your test.
How to do it...
We will build our MyFirstClass class from the ground up. There are several steps one must follow to implement a class; they are follows:
Define the class as follows:
Class MyFirstClass
Next, we define the class fields. Fields are like regular variables, but encapsulated within the namespace defined by the class. The fields can be private or public. A private field can be accessed only by class members. A public field can be accessed from any block of code. The code is as follows:
Class MyFirstClass
Private m_sMyPrivateString
Private m_oMyPrivateObject
Public m_iMyPublicInteger
End Class
It is a matter of convention to use the prefix m_ for class member fields; and str for string, int for integer, obj for Object, flt for Float, bln for Boolean, chr for Character, lng for Long, and dbl for Double, to distinguish between fields of different data types. For examples of other prefixes to represent additional data types, please refer to sites such as https://en.wikipedia.org/wiki/Hungarian_notation.
Hence, the private fields' m_sMyPrivateString and m_oMyPrivateObject will be accessible only from within the class methods, properties, and subroutines. The public field m_iMyPublicInteger will be accessible from any part of the code that will have a reference to an instance of the MyFirstClass class; and it can also allow partial or full access to private fields, by implementing public properties.
By default, within a script file, VBScript treats as public identifiers such as function and subroutines and any constant or variable defined with Const and Dim respectively,, even if not explicitly defined. When associating function libraries to UFT, one can limit access to specific globally defined identifiers, by preceding them with the keyword Private.
The same applies to members of a class, function, sub, and property. Class fields must be preceded either by Public or Private; the public scope is not assumed by VBScript, and failing to precede a field identifier with its access scope will result in a syntax error. Remember that, by default, VBScript creates a new variable if the explicit option is used at the script level to force explicit declaration of all variables in that script level.
Next, we define the class properties. A property is a code structure used to selectively provide access to a class' private member fields. Hence, a property is often referred to as a getter (to allow for data retrieval) or setter (to allow for data change).
A property is a special case in VBScript; it is the only code structure that allows for a duplicate identifier. That is, one can have a Property Get and a Property Let procedure (or Property Set, to be used when the member field actually is meant to store a reference to an instance of another class) with the same identifier. Note that Property Let and Property Set accept a mandatory argument. For example:
Class MyFirstClass
Private m_sMyPrivateString
Private m_oMyPrivateObject
Public m_iMyPublicInteger
Property Get MyPrivateString()
MyPrivateString = m_sMyPrivateString
End Property
Property Let MyPrivateString(ByVal str)
m_sMyPrivateString = str
End Property
Property Get MyPrivateObject()
Set MyPrivateObject = m_oMyPrivateObject
End Property
Private Property Set MyPrivateObject(ByRef obj)
Set m_oMyPrivateObject = obj
End Property
End Class
The public field m_iMyPublicInteger can be accessed from any code block, so defining a getter and setter (as properties are often referred to) for such a field is optional. However, it is a good practice to define fields as private and explicitly provide access through public properties. For fields that are for exclusive use of the class members, one can define the properties as private. In such a case, usually, the setter (Property Let or Property Set) would be defined as private, while the getter (Property Get) would be defined as public. This way, one can prevent other code components from making changes to the internal fields of the class to ensure data integrity and validity.
Define the class methods and subroutines. A method is a function, which is a member of a class. Like fields and properties, methods (as well as subroutines) can be Private or Public. For example:
Class MyFirstClass
'… Continued
Private Function MyPrivateFunction(ByVal str)
MsgBox TypeName(me) & " – Private Func: " & str
MyPrivateFunction = 0
End Function
Function MyPublicFunction(ByVal str)
MsgBox TypeName(me) & " – Public Func: " & str
MyPublicFunction = 0
End Function
Sub MyPublicSub(ByVal str)
MsgBox TypeName(me) & " – Public Sub: " & str
End Sub
End Class
Keep in mind that subroutines do not return a value. Functions by design should not return a value, but they can be implemented as a subroutine. A better way is to, in any case, have a function return a value that tells the caller if it executed properly or not (usually zero (0) for no errors and one (1) for any fault). Recall that a function that is not explicitly assigned a value function and is not explicitly assigned a value, will return empty, which may cause problems if the caller attempts to evaluate the returned value.
Now, we define how to initialize the class when a VBScript object is instantiated:
Set obj = New MyFirstClass
The Initialize event takes place at the time the object is created. It is possible to add code that we wish to execute every time an object is created. So, now define the standard private subroutine Class_Initialize, sometimes referred to (albeit only by analogy) as the constructor of the class. If implemented, the code will automatically be executed during the Initialize event. For example, if we add the following code to our class:
Private Sub Class_Initialize
MsgBox TypeName(me) & " started"
End Sub
Now, every time the Set obj = New MyFirstClass statement is executed, the following message will be displayed:
Define how to finalize the class. We finalize a class when a VBScript object is disposed of (as follows), or when the script exits the current scope (such as when a local object is disposed when a function returns control to the caller), or a global object is disposed (when UFT ends its run session):
Set obj = Nothing
The Finalize event takes place at the time when the object is removed from memory. It is possible to add code that we wish to execute, every time an object is disposed of. If so, then define the standard private subroutine Class_Terminate, sometimes referred to (albeit only by analogy) as the destructor of the class. If implemented, the code will automatically be executed during the Finalize event. For example, if we add the following code to our class:
Private Sub Class_Terminate
MsgBox TypeName(me) & " ended"
End Sub
Now, every time the Set obj = Nothing statement is executed, the following message will be displayed:
Invoking (calling) a class method or property is done as follows:
'Declare variables
Dim obj, var
'Calling MyPublicFunction
obj.MyPublicFunction("Hello")
'Retrieving the value of m_sMyPrivateString
var = obj.MyPrivateString
'Setting the value of m_sMyPrivateString
obj.MyPrivateString = "My String"
Note that the usage of the public members is done by using the syntax obj.<method or property name>, where obj is the variable holding the reference to the object of class. The dot operator (.) after the variable identifier provides access to the public members of the class. Private members can be called only by other members of the class, and this is done like any other regular function call.
VBScript supports classes with a default behavior. To utilize this feature, we need to define a single default method or property that will be invoked every time an object of the class is referred to, without specifying which method or property to call. For example, if we define the public method MyPublicFunction as default:
Public Default Function MyPublicFunction(ByVal str)
MsgBox TypeName(me) & " – Public Func: " & str
MyPublicFunction = 0
End Function
Now, the following statements would invoke the MyPublicFunction method implicitly:
Set obj = New MyFirstClass
obj("Hello")
This is exactly the same as if we called the MyPublicFunction method explicitly:
Set obj = New MyFirstClass
obj.MyPublicFunction("Hello")
Contrary to the usual standard for such functions, a default method or property must be explicitly defined as public.
Now, we will see how to add a constructor-like function. When using classes stored in function libraries, UFT (know as QTP in previous versions), cannot create an object using the New operator inside a test Action.
In general, the reason is linked to the fact that UFT uses a wrapper on top of WSH, which actually executes the VBScript (VBS 5.6) code. Therefore, in order to create instances of such a custom class, we need to use a kind of constructor function that will perform the New operation from the proper memory namespace. Add the following generic constructor to your function library:
Function Constructor(ByVal sClass)
Dim obj
On Error Resume Next
'Get instance of sClass
Execute "Set obj = New [" & sClass & "]"
If Err.Number <> 0 Then
Set obj = Nothing
Reporter.ReportEvent micFail, "Constructor", "Failed to create an instance of class '" & sClass & "'."
End If
Set Constructor = obj
End Function
We will then instantiate the object from the UFT Action, as follows:
Set obj = Constructor("MyFirstClass")
Consequently, use the object reference in the same fashion as seen in the previous line of code:
obj.MyPublicFunction("Hello")
How it works...
As mentioned earlier, using the internal public fields, methods, subroutines, and properties is done using a variable followed by the dot operator and the relevant identifier (for example, the function name).
As to the constructor, it accepts a string with the name of a class as an argument, and attempts to create an instance of the given class. By using the Execute command (which performs any string containing valid VBScript syntax), it tries to set the variable obj with a new reference to an instance of sClass. Hence, we can handle any custom class with this function. If the class cannot be instantiated (for instance, because the string passed to the function is faulty, the function library is not associated to the test, or there is a syntax error in the function library), then an error would arise, which is gracefully handled by using the error-handling mechanism, leading to the function returning nothing. Otherwise, the function will return a valid reference to the newly created object.
See also
The following articles at www.advancedqtp.com are part of a wider collection, which also discuss classes and code design in depth:
An article by Yaron Assa at http://www.advancedqtp.com/introduction-to-classes
An article by Yaron Assa at http://www.advancedqtp.com/introduction-to-code-design
An article by Yaron Assa at http://www.advancedqtp.com/introduction-to-design-patterns
Implementing a simple search class
In this recipe, we will see how to create a class that can be used to execute a search on Google.
Getting ready
From the File menu, navigate to New | Test, and name the new test SimpleSearch. Then create a new function library by navigating to New | Function Library, or use the Alt + Shift + N shortcut. Name the new function library cls.Google.vbs and associate it with your test.
How to do it...
Proceed with the following steps:
Define an environment variable as OPEN_URL.
Insert the following code in the new library:
Class GoogleSearch
Public Function DoSearch(ByVal sQuery)
With me.Page_
.WebEdit("name:=q").Set sQuery
.WebButton("html id:=gbqfba").Click
End With
me.Browser_.Sync
If me.Results.WaitProperty("visible", 1, 10000) Then
DoSearch = GetNumResults()
Else
DoSearch = 0
Reporter.ReportEvent micFail, TypeName(Me), "Search did not retrieve results until timeout"
End If
End Function
Public Function GetNumResults()
Dim tmpStr
tmpStr = me.Results.GetROProperty("innertext")
tmpStr = Split(tmpStr, " ")
GetNumResults = CLng(tmpStr(1)) 'Assumes the number is always in the second entry
End Function
Public Property Get Browser_()
Set Browser_ = Browser(me.Title)
End Property
Public Property Get Page_()
Set Page_ = me.Browser_.Page(me.Title)
End Property
Public Property Get Results()
Set Results = me.Page_.WebElement(me.ResultsId)
End Property
Public Property Get ResultsId()
ResultsId = "html id:=resultStats"
End Property
Public Property Get Title()
Title = "title:=.*Google.*"
End Property
Private Sub Class_Initialize
If Not me.Browser_.Exist(0) Then
SystemUtil.Run "iexplore.exe", Environment("OPEN_URL")
Reporter.Filter = rfEnableErrorsOnly
While Not Browser_.Exist(0)
Wait 0, 50
Wend
Reporter.Filter = rfEnableAll
Reporter.ReportEvent micDone, TypeName(Me), "Opened browser"
Else
Reporter.ReportEvent micDone, TypeName(Me), "Browser was already open"
End If
End Sub
Private Sub Class_Terminate
If me.Browser_.Exist(0) Then
me.Browser_.Close
Reporter.Filter = rfEnableErrorsOnly
While me.Browser_.Exist(0)
wait 0, 50
Wend
Reporter.Filter = rfEnableAll
Reporter.ReportEvent micDone, TypeName(Me), "Closed browser"
End If
End Sub
End Class
In Action, write the following code:
Dim oGoogleSearch
Dim oListResults
Dim oDicSearches
Dim iNumResults
Dim sMaxResults
Dim iMaxResults
'--- Create these objects only in the first iteration
If Not LCase(TypeName(oListResults)) = "arraylist" Then
Set oListResults = CreateObject("System.Collections.ArrayList")
End If
If Not LCase(TypeName(oDicSearches)) = "Dictionary" Then
Set oDicSearches = CreateObject("Scripting.Dictionary")
End If
'--- Get a fresh instance of GoogleSearch
Set oGoogleSearch = GetGoogleSearch()
'--- Get search term from the DataTable for each action iteration
sToSearch = DataTable("Query", dtLocalSheet)
iNumResults = oGoogleSearch.DoSearch(sToSearch)
'--- Store the results of the current iteration
'--- Store the number of results
oListResults.Add iNumResults
'--- Store the search term attached to the number of results as key (if not exists)
If Not oDicSearches.Exists(iNumResults) Then
oDicSearches.Add iNumResults, sToSearch
End If
'Last iteration (assuming we always run on all rows), so perform the comparison between the different searches
If CInt(Environment("ActionIteration")) = DataTable.LocalSheet.GetRowCount Then
'Sort the results ascending
oListResults.Sort
'Get the last item which is the largest
iMaxResults = oListResults.item(oListResults.Count-1)
'Print to the Output pane for debugging
Print iMaxResults
'Get the search text which got the most results
sMaxResults = oDicSearches(iMaxResults)
'Report result
Reporter.ReportEvent micDone, "Max search", sMaxResults & " got " & iMaxResults
'Dispose of the objects used
Set oListResults = Nothing
Set oDicSearches = Nothing
Set oGoogleSearch = Nothing
End If
In the local datasheet, create a parameter named Query and enter several values to be used in the test as search terms.
Next, from the UFT home page navigate to View | Test Flow, and then right-click with the mouse on the Action component in the graphic display, then select Action Call Properties and set the Action to run on all rows.
How it works...
The Action takes care to preserve the data collected through the iterations in the array list oListResults and the dictionary oDicSearches. It checks if it reaches the last iteration after each search is done. Upon reaching the last iteration, it analyses the data to decide which term yielded the most results. A more detailed description of the workings of the code can be seen as follows.
First, we create an instance of the GoogleSearch class, and the Class_Initialize subroutine automatically checks if the browser is not already open. If not open, Class_Initialize opens it with the SystemUtil.Run command and waits until it is open at the web address defined in Environment("OPEN_URL").
The Title property always returns the value of the Descriptive Programming (DP) value required to identify the Google browser and page.
The Browser_, Page_, and Results properties always return a reference to the Google browser, page, and WebElement respectively, which hold the text with the search results.
After the browser is open, we retrieve the search term from the local DataTable parameter Query and call the GoogleSearch DoSearch method with the search term string as parameter. The DoSearch method returns a value with the number of results, which are given by the internal method GetNumResults.
In the Action, we store the number itself and add to the dictionary, an entry with this number as the key and the search term as the value.
When the last iteration is reached, an analysis of the results is automatically done by invoking the Sort method of oListResults ArrayList, getting the last item (the greatest), and then retrieving the search term associated with this number from the dictionary; it reports the result.
At last, we dispose off all the objects used, and then the Class_Terminate subroutine automatically checks if the browser is open. If open, then the Class_Terminate subroutine closes the browser.
Implementing a generic Login class
In this recipe, we will see how to implement a generic Login class. The class captures both, the GUI structure and the processes that are common to all applications with regard to their user access module. It is agnostic to the particular object classes, their technologies, and other identification properties. The class shown here implements the command wrapper design pattern, as it encapsulates a process (Login) with the main default method (Run).
Getting ready
You can use the same function library cls.Google.vbs as in the previous recipe Implementing a simple search class, or create a new one (for instance, cls.Login.vbs) and associate it with your test.
How to do it...
In the function library, we will write the following code to define the class Login:
Class Login
Private m_wndContainer 'Such as a Browser, Window, SwfWindow
Private m_wndLoginForm 'Such as a Page, Dialog, SwfWindow
Private m_txtUsername 'Such as a WebEdit, WinEdit, SwfEdit
Private m_txtIdField 'Such as a WebEdit, WinEdit, SwfEdit
Private m_txtPassword 'Such as a WebEdit, WinEdit, SwfEdit
Private m_chkRemember 'Such as a WebCheckbox, WinCheckbox, SwfCheckbox
Private m_btnLogin 'Such as a WebEdit, WinEdit, SwfEdit
End Class
These fields define the test objects, which are required for any Login class, and the following fields are used to keep runtime data for the report:
Public Status 'As Integer
Public Info 'As String
The Run function is defined as a Default method that accepts a Dictionary as argument. This way, we can pass a set of named arguments, some of which are optional, such as timeout.
Public Default Function Run(ByVal ArgsDic)
'Check if the timeout parameter was passed, if not assign it 10 seconds
If Not ArgsDic.Exists("timeout") Then ArgsDic.Add "timeout", 10
'Check if the client window exists
If Not me.Container.Exist(ArgsDic("timeout")) Then
me.Status = micFail
me.Info = "Failed to detect login browser/dialog/window."
Exit Function
End If
'Set the Username
me.Username.Set ArgsDic("Username")
'If the login form has an additional mandatory field
If me.IdField.Exist(ArgsDic("timeout")) And ArgsDic.Exists("IdField") Then
me.IdField.Set ArgsDic("IdField")
End If
'Set the password
me.Password.SetSecure ArgsDic("Password")
'It is a common practice that Login forms have a checkbox to keep the user logged-in if set ON
If me.Remember.Exist(ArgsDic("timeout")) And ArgsDic.Exists("Remember") Then
me.Remember.Set ArgsDic("Remember")
End If
me.LoginButton.Click
End Function
The Run method actually performs the login procedure, setting the username and password, as well as checking or unchecking the Remember Me or Keep me Logged In checkbox according to the argument passed with the ArgsDic dictionary.
The Initialize method accepts Dictionary just like the Run method. However, in this case, we pass the actual TOs with which we wish to perform the login procedure. This way, we can actually utilize the class for any Login form, whatever the technology used to develop it. We can say that the class is technology agnostic. The parent client dialog/browser/window of the objects is retrieved using the GetTOProperty("parent") statement:
Function Initialize(ByVal ArgsDic)
Set m_txtUsername = ArgsDic("Username")
Set m_txtIdField = ArgsDic("IdField")
Set m_txtPassword = ArgsDic("Password")
Set m_btnLogin = ArgsDic("LoginButton")
Set m_chkRemember = ArgsDic("Remember")
'Get Parents
Set m_wndLoginForm = me.Username.GetTOProperty("parent")
Set m_wndContainer = me.LoginForm.GetTOProperty("parent")
End Function
In addition, here you can see the following properties used in the class for better readability:
Property Get Container()
Set Container = m_wndContainer
End Property
Property Get LoginForm()
Set LoginForm = m_wndLoginForm
End Property
Property Get Username()
Set Username = m_txtUsername
End Property
Property Get IdField()
Set IdField = m_txtIdField
End Property
Property Get Password()
Set Password = m_txtPassword
End Property
Property Get Remember()
Set Remember = m_chkRemember
End Property
Property Get LoginButton()
Set LoginButton = m_btnLogin
End Property
Private Sub Class_Initialize()
'TODO: Additional initialization code here
End Sub
Private Sub Class_Terminate()
'TODO: Additional finalization code here
End Sub
We will also add a custom function to override the WinEdit and WinEditor Type methods:
Function WinEditSet(ByRef obj, ByVal str)
obj.Type str
End Function
This way, no matter which technology the textbox belongs to, the Set method will work seamlessly.
To actually test the Login class, write the following code in the Test Action (this time we assume that the Login form was already opened by another procedure):
Dim ArgsDic, oLogin
'Register the set method for the WinEdit and WinEditor
RegisterUserFunc "WinEdit", "WinEditSet", "Set"
RegisterUserFunc "WinEditor", "WinEditSet", "Set"
'Create a Dictionary object
Set ArgsDic = CreateObject("Scripting.Dictionary")
'Create a Login object
Set oLogin = New Login
'Add the test objects to the Dictionary
With ArgsDic
.Add "Username", Browser("Gmail").Page("Gmail").WebEdit("txtUsername")
.Add "Password", Browser("Gmail").Page("Gmail").WebEdit("txtPassword")
.Add "Remember", Browser("Gmail").Page("Gmail") .WebCheckbox("chkRemember")
.Add "LoginButton", Browser("Gmail").Page("Gmail").WebButton("btnLogin")
End With
'Initialize the Login class
oLogin.Initialize(ArgsDic)
'Initialize the dictionary to pass the arguments to the login
ArgsDic.RemoveAll
With ArgsDic
.Add "Username", "myuser"
.Add "Password", "myencriptedpassword"
.Add "Remember", "OFF"
End With
'Login
oLogin.Run(ArgsDic) 'or: oLogin(ArgsDic)
'Report result
Reporter.ReportEvent oLogin.Status, "Login", "Ended with " & GetStatusText(oLogin.Status) & "." & vbNewLine & oStatus.Info
'Dispose of the objects
Set oLogin = Nothing
Set ArgsDic = Nothing
How it works...
Here we will not delve into the parts of the code already explained in the Implementing a simple search class recipe. Let's see what we did in this recipe:
We registered the custom function WinEditSet to the WinEdit and WinEditor TO classes using RegisterUserFunc. As discussed previously, this will make every call to the method set to be rerouted to our custom function, resulting in applying the correct method to the Standard Windows text fields.
Next, we created the objects we need, a Dictionary object and a Login object.
Then, we added the required test objects to Dictionary, and then invoked its Initialize method, passing the Dictionary as the argument.
We cleared Dictionary and then added to it the values needed for actually executing the login (Username, Password, and the whether to remember the user or keep logged in checkboxes usually used in Login forms).
We called the Run method for the Login class with the newly populated Dictionary.
Later, we reported the result by taking the Status and Info public fields from the oLogin object.
At the end of the script, we unregistered the custom function from all classes in the environment (StdWin in this case).
Implementing function pointers
What is a function pointer? A function pointer is a variable that stores the memory address of a block of code that is programmed to fulfill a specific function. Function pointers are useful to avoid complex switch case structures. Instead, they support direct access in runtime to previously loaded functions or class methods. This enables the construction of callback functions. A callback is, in essence, an executable code that is passed as an argument to a function. This enables more generic coding, by having lower-level modules calling higher-level functions or subroutines.
This recipe will describe how to implement function pointers in VBScript, a scripting language that does not natively support the usage of pointers.
Getting ready
Create a new function library (for instance, cls.FunctionPointers.vbs) and associate it with your test.
How to do it...
Write the following code in the function library:
Class WebEditSet
Public Default Function Run(ByRef obj, ByVal sText)
On Error Resume Next
Run = 1 'micFail (pessimistic initialization)
Select Case True
Case obj.Exist(0) And _
obj.GetROProperty("visible") And _
obj.GetROProperty("enabled")
'Perform the set operation
obj.Set(sText)
Case Else
Reporter.ReportEvent micWarning, TypeName(me), "Object not available."
Exit Function
End Select
If Err.Number = 0 Then
Run = 0 'micPass
End If
End Function
End Class
Write the following code in Action:
Dim pFunctiontion
Set pFunctiontion = New WebEditSet
Reporter.ReportEvent pFunctiontion(Browser("Google").Page("Google") .WebEdit("q"), "UFT"), "Set the Google Search WebEdit", "Done"
How it works...
The WebEditSet class actually implements the command wrapper design pattern (refer to also the Implementing a generic Login class recipe). This recipe also demonstrates an alternative way of overriding any native UFT TO method without recurring to the RegisterUserFunc method.
First, we create an instance of the WebEditSet class and set the reference to our pFunctiontion variable. Note that the Run method of WebEditSet is declared as a default function, so we can invoke its execution by merely referring to the object reference, as is done with the statement pFunctiontion in the last line of code in the How to do it… section. This way, pFunctiontion actually functions as if it were a function pointer. Let us take a close look at the following line of code, beginning with Reporter.ReportEvent:
Reporter.ReportEvent pFunc(Browser("Google").Page("Google").WebEdit("q"), "UFT"), "Set the Google Search WebEdit", "Done"
We call the ReportEvent method of Reporter, and as its first parameter, instead of a status constant such as micPass or micFail, we pass pFunctiontion and the arguments accepted by the Run method (the target TO and its parameter, a string). This way of using the function pointer actually implements a kind of callback. The value returned by the Run method of WebEditSet will determine whether UFT will report a success or failure in regard to the Set operation. It will return through the call invoked by accessing the function pointer.
See also
The following articles are part of a wider collection at www.advancedqtp.com, which also discusses function pointers in depth:
An article by Meir Bar-Tal at http://www.advancedqtp.com/ function-pointers-in-vb-script-revised
An article by Meir Bar-Tal at http://www.advancedqtp.com/using-to-custom-property-as-function-pointer
Summary
In this article, we learned how to implement a general class; basic concepts and the syntax required by VBScript to implement a class. Then we saw how to implement a simple class that can be used to execute a search on Google and a generic Login class. We also saw how to implement function pointers in VBScript along with various links to the articles that discusses function pointers.
Resources for Article:
Further resources on this subject:
DOM and QTP [Article]
Getting Started with Selenium Grid [Article]
Quick Start into Selenium Tests [Article]
Read more