preload

Overload your VBScript functions (My article on QAGuild)

Posted by Albert Gareev on Jun 25, 2009 | Categories: LinksMy Articles

Publication URL:  http://www.qaguild.com/weekly_archives.php?UID=60

Code examples package: overload-codeexamples1

Overload your VBScript functions

 
      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.

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


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

 

Select Case 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:

Service Functions – String (QTP, VBScript)

See usage examples below.

‘Example (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"))

 

‘Example (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.

Related Posts

Service Functions – DotNetFactory (QTP, VBScript)


Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported
This work by Albert Gareev is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported.