Overload your VBScript functions (My article on QAGuild)
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.
- Target state: Maximized. Arguments: state = “MAX”
- Target state: Minimized. Arguments: state = “MIN”
- Target state: Resized. Arguments: state = “RESIZE”. Optional: Width (400), Height (300)
- Target state: Moved. Arguments: state = “MOVE”. Optional: X (0), Y (0)
- Target state: Closed. Arguments: state = “CLOSE”
- 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.
- Location: as defined or (0,0)
- Dimensions: as defined or (100,20)
- Prompt text: as defined or nothing
- Items: as defined or none
- 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