Albert Gareev

Overload your VBScript functions

Bookmark and Share Bookmark and Share

     While implementing Automated Functional Testing solutions I often face the challenge creating the scripts somewhat “intelligent”, capable of making decisions and recognizing different logical patterns on the run. One of the major steps on this path is breaking the boundaries of hard-coded logic and getting to the abstract programming level, – and the main abstract programming paradigm is Object-Oriented Programming.

     Term function overloading in Object-Oriented Programming means implementation of more than one function under the same call name. The general advantages of this approach are the following

  • The code is more structured – following best practices
  • Programming is more abstract – makes it closer to human logic
  • Ability to implement default and optional parameters
  • One less function to remember – better convenience in programming and maintenance

     The main challenge implementing back-end of this approach is determining which function meant to be called. Something specific and unique in the context of the call must help to identify that – and call parameters serve this purpose.

     VBScript is a simplified language with a lot of restrictions but its true power is an open support of the surrounding technologies. While such a core Object-Oriented features like polymorphism (that includes function overloading and overriding) and inheritance are not supported, external object families could be easily used within VBScript.
     One of the most useful objects is Dictionary.

Dictionary Object

     Dictionary object represents a dynamic associative array with the following features.

  • Item index (key) could be number or string (and must be unique)
  • Item value could be of scalar type (single value, like number), or array type, string type, or even object reference
  • Order of items doesn’t matter
  • Items could be added or removed dynamically
  • A key could be checked for presence
  • Count of items is known
  • The all defined keys could be exported into array

     The presented features allow using Associative Programming technique and that in turn helps implementing polymorphism in function calls.

Objective (Example 1)

     Implement QTP VBScript function changing Web Browser Window state from uncertain to the specified one.

  1. Target state: Maximized
    Arguments: state = “MAX”
  2. Target state: Minimized
    Arguments: state = “MIN”
  3. Target state: Resized
    Arguments: state = “RESIZE”
    Optional: Width (400), Height (300)
  4. Target state: Moved
    Arguments: state = “MOVE”
    Optional: X (0), Y (0)
  5. Target state: Closed
    Arguments: state = “CLOSE”
  6. Target state: Navigated to address
    Arguments: state = “NAVIGATE”
    Optional: URL

Implementation

     Declare the function as public; define local variables; initialize.

Public Function SetBrowser(ByRef objBrowser, ByVal objParameter)
                Dim boolRC
                Dim objBrowserWin, hWnd, intX, intY
                Dim sTypeName, sState, sURL
               
                'Verify parameters
                sTypeName = TypeName (objParameter)
                If sTypeName <>  "Dictionary" Then
                                Set objParameter = CreateObject("Scripting.Dictionary")
                End If

     Verify target object (Browser Window) exists.

boolRC = objBrowser.Exist(0)
                If Not boolRC Then
                                SetBrowser = FALSE
                                Exit Function
                End If

     Retrieve main argument and initialize it to default value if nothing was passed in.

'Retrieve arguments
                sState = UCase(objParameter.Item("state"))

                If sState = "" Then
                                sState = "MAX"
                End If

      Get Windows Handle of the Browser.

'Get Browser as the Window
                Set objBrowserWin = objBrowser.Object
                hWnd = objBrowserWin.HWND

      Perform operations based on the Actual State, Required State and Optional Parameters provided. Close the function.

Select Case UCase(sState)
                                Case "MAX"
                                                On Error Resume Next
                                                                boolRC = Window ("hwnd:=" & hWnd).GetROProperty("maximized")
                                                                If Not boolRC Then Window ("hwnd:=" & hWnd).Maximize
                                                                Window ("hwnd:=" & hWnd).Activate
                                                On Error GoTo 0
                                Case "MIN"
                                                On Error Resume Next
                                                                boolRC = Window ("hwnd:=" & hWnd).GetROProperty("minimized")
                                                                If Not boolRC Then Window ("hwnd:=" & hWnd).Minimize
                                                On Error GoTo 0
                                Case "RESIZE"
                                                intX = IntVal(objParameter.Item("p.width"))
                                                If intX <= 0 Then intX = 400
                                                intY = IntVal(objParameter.Item("p.height"))
                                                If intY <= 0 Then intY = 300
                                                On Error Resume Next
                                                                boolRC = Window ("hwnd:=" & hWnd).GetROProperty("minimized") OR Window ("hwnd:=" & hWnd).GetROProperty("maximized")
                                                                If boolRC Then Window ("hwnd:=" & hWnd).Restore
                                                                Window ("hwnd:=" & hWnd).Resize intX, intY
                                                                Window ("hwnd:=" & hWnd).Activate
                                                On Error GoTo 0
                                Case "MOVE"
                                                intX = IntVal(objParameter.Item("p.x"))
                                                If intX < 0 Then intX = 0
                                                intY = IntVal(objParameter.Item("p.y"))
                                                If intY < 0 Then intY = 0
                                                On Error Resume Next
                                                                boolRC = Window ("hwnd:=" & hWnd).GetROProperty("minimized") OR Window ("hwnd:=" & hWnd).GetROProperty("maximized")
                                                                If boolRC Then Window ("hwnd:=" & hWnd).Restore
                                                                Window ("hwnd:=" & hWnd).Move intX, intY
                                                                Window ("hwnd:=" & hWnd).Activate
                                                On Error GoTo 0
                                Case "CLOSE"
                                                On Error Resume Next
                                                                objBrowser.Close
                                                On Error GoTo 0
                                Case "NAVIGATE"
                                                sURL = objParameter.Item("url")
                                                On Error Resume Next
                                                                objBrowser.Navigate  sURL
                                                On Error GoTo 0
                                Case Else
                                                'do nothing
                End Select

                Set objBrowserWin = Nothing

                SetBrowser = TRUE
End Function

Additional Explanations

     To avoid execution to be broken GUI interaction is wrapped by On Error statement.
     Since changing the browser state is a service type operation (not a business functionality testing type) – the function does not report success or fail except of      passing back the result.
     Certain operations (resize, move) require specific window state – so the function sets the browser window to a normal state first.

Objective (Example 2)

     Implement QTP VBScript function creating DOT NET GUI Object (ComboBox) initializing specified properties with defined or default values.

  1. Location: as defined or (0,0)
  2. Dimensions: as defined or (100,20)
  3. Prompt text: as defined or nothing
  4. Items: as defined or none
  5. Selected item: optionally defined by index or by value

Implementation

     Declare the function as public; define local variables; initialize. Retrieve passed in parameters and initialize with default values.

Public Function CreateComboBox(ByVal objItems, ByVal objParameter)
   Dim sText, intWidth, intHeight, intMaxLength
   Dim intLeft, intTop
   Dim objComboBox, objComboBoxStyle
   Dim Iter, dvKeys, sItem, intIndex

                'Verify parameters
                If TypeName (objParameter) <>  "Dictionary" Then
                                Set objParameter = CreateObject("Scripting.Dictionary")
                End If

                sText = objParameter.Item("p.text")
                intWidth = InitLong(objParameter.Item("p.width"), 100)
                intHeight = InitLong(objParameter.Item("p.height"), 20)
                intLeft = IntVal(objParameter.Item("p.left"))
                intTop = IntVal(objParameter.Item("p.top"))

     Create object instance and set the properties.

                Set objComboBoxStyle = DotNetFactory.CreateInstance("System.Windows.Forms.ComboBoxStyle", "System.Windows.Forms")
                Set objComboBox = DotNetFactory.CreateInstance("System.Windows.Forms.ComboBox", "System.Windows.Forms")

                objComboBox.Text = sText
                objComboBox.Width = intWidth
                objComboBox.Height = intHeight
                objComboBox.Left = intLeft
                objComboBox.Top = intTop

                objComboBox.DropDownStyle = objComboBoxStyle.DropDownList
                Set objComboBoxStyle = Nothing

  Define selectable items. If none specified – exit the function.

             If TypeName (objItems) <>  "Dictionary" Then
                                Set CreateComboBox = objComboBox
                                Exit Function
                End If

                dvKeys = objItems.Keys()

                For Iter = 0 To UBound(dvKeys)
                                sItem = objItems.Item(dvKeys(Iter))
                                If sItem <> "" Then
                                                objComboBox.Items.Add(sItem)
                                End If
                Next

  If specified – set the item selected by default. Close the function.

           If objParameter.Exists("p.index") Then
                                intIndex = IntVal(objParameter.Item("p.index"))
                                If intIndex <= UBound(dvKeys) Then
                                                objComboBox.SelectedIndex =  intIndex
                                End If
                End If

                If objParameter.Exists("p.item") Then
                                sItem = objParameter.Item("p.item")
                                intIndex = objComboBox.FindStringExact(sItem)
                                If intIndex <> - 1 Then
                                                objComboBox.SelectedIndex =  CInt(intIndex)
                                End If
                End If
               
                Set CreateComboBox = objComboBox
End Function

Additional Explanations

     Certain low-level functions were used in the presented code examples. Please see below the source code.

         Public Function IntVal(ByVal sVal)
   If isNumeric(sVal) Then
                   IntVal = CLng(sVal)
                Else
                   IntVal = 0
   End If
End Function

Public Function InitLong(ByVal sActualValue, ByVal DefaultValue)
                DefaultValue = IntVal(DefaultValue)
  
                If sActualValue = "" Then
                                InitLong = DefaultValue
                Else
                   If isNumeric(sActualValue) Then
                      InitLong = CLng(sActualValue)
                   Else
                      InitLong = DefaultValue
                   End If
                End If
End Function

Line-up the call statement

     Additional service function allows calling of the overloaded function in a single line of the code.

       Public Function AssociateParameters(ByVal strParams)
                Dim objDictionary, Values, strPair, Pair
                Dim intCount, i
               
                strParams = Replace(strParams, "\,", Chr(176))
                strParams = Replace(strParams, "\=", Chr(187))

                Values  = Split(strParams, ",")
                intCount = UBound(Values)
                Set objDictionary = CreateObject("Scripting.Dictionary")

                For i=0 To intCount
                                strPair = Trim(Values(i))
                                Pair = Split(strPair, "=")
                                If UBound(Pair) = 1 Then
                                                If Not objDictionary.Exists(Trim(Pair(0))) Then
                                                                objDictionary.Add Trim(Pair(0)), Replace(Replace(Trim(Pair(1)), Chr(176), ","), Chr(187), "=")
                                                Else        
                                                                objDictionary.Item(Trim(Pair(0))) = Replace(Replace(Trim(Pair(1)), Chr(176), ","), Chr(187), "=")
                                                End If
                                End If
                Next
               
                 Set AssociateParameters =  objDictionary
End Function

     See usage examples below.

   ‘(1)
‘Make sure you have the browser up and enabled
‘Do not confuse object recognition by running multiple browser windows

Set objBrowser = Browser("title:=.*","location:=0")
boolRC = SetBrowser(objBrowser, AssociateParameters("state = max"))
Wait 2
boolRC = SetBrowser(objBrowser, AssociateParameters("state = min"))
Wait 2
boolRC = SetBrowser(objBrowser, AssociateParameters("state = resize, p.width = 800, p.height = 600"))
Wait 2
boolRC = SetBrowser(objBrowser, AssociateParameters("state = move, p.x = 100, p.y = 100"))
Wait 2
boolRC = SetBrowser(objBrowser, AssociateParameters("state = close"))

‘(2)
Set objSelectEnvironment = CreateComboBox(AssociateParameters("1 = DEV1, 2 = DEV2, 3 = UAT"), AssociateParameters("p.prompt = Test Environment, p.left = 25, p.top = 50, p.item = DEV2"))
‘Now you can place ComboBox on the form if you have one

Analysis and conclusion

     Overloading of functions is possible in VBScript and it does not actually require defining two separate functions.
     Applying to Test Automation needs, the following common types of operations are the good candidates for implementation as overloaded functions.

  • Perform different operations on the same object
  • Perform same operations with the different types of input data
  • Produce different output from the same input
  • Initialize complicated data records in a generic way by implementing optional and default parameters
  • All of the above in combinations

     There is no need in spending hours guessing the all possible operations to cover in the overloaded function: they could be easily added later.
     Since order of the pass-in parameters doesn’t matter new parameters won’t impact existing ones, and existing ones could be dynamically redefined based on the context. That creates additional convenience and reduces cost on tracking back changes in case of code refactoring.
     Business functionality testing code becomes more compact and thus more readable and better maintainable. Minor level decisions are done automatically without putting additional coding efforts.
     Combining function overloading with Layered Programming approach brings to an even higher level of abstraction in automated decision-making, and use of XML allows using of even more complicated associations – but this implementation is out of scope of the original article

About the author

Albert Gareev holds Master’s degree in IT, and has been working for over 12 years in Software Development and Quality Assurance. Passionate for QA Automation he successfully accomplished multiple large scale projects in the past 5 years, implementing Test Architectures with WinRunner and Quick Test Pro. He is currently working in a technical leading role coordinating in-house and off-shore automation projects while staying a hands-on and innovative professional.

Review Comments

hemanth said,
August 11, 2009, 1:04am
 
it sis good
 

 

Comments:
Name:
E-mail:
Enter the text shown :
 
 

 


Return to Weekly Article