November 4, 2008

How to make use of Function Pointers in a Keyword Driven Approach


A commonly used automated test methodology is working with user defined keywords. The tester defines the keyword with parameters next to it and the automated testtool processes this keyword and executes the corresponding function.

There are two ways I know of to make this work in QTP, both with advantages and drawbacks.
A "select case" construction gives you flexibility, but with every keyword you have to add a case manually.

Select Case KeyWord
   Case "login" call login()
   Case "enter user" call enterUser()
   Case Else msgbox "Invalid Keyword: '" & KeyWord & "'"
End Select

Using an eval or execute can lead to type mismatch errors. You can circumvent this by putting in an "On error resume next", but this will lead to no error handling at all by the VBScript engine. Any undefined variable normally results in a "Variable is undefined" error, which can be a great help. You are losing that with an "on error resume next" statement.
Also, a execute or eval does not return a value or object, unless you are capturing that with a variable in the string of the execute itself.

On error resume next
Execute ("TestResult = " & KeyWord & "()") ' -> Any error within the processing of the KeyWord will not be trapped.
On error goto 0

In this post, I will show you another way to dynamically assign keywords to functions in a fast way, with trapping of invalid keywords on a soft way (programmatically and not by the VBScript engine), and keeping the normal error behaviour. This can be done by creating a function pointer to the function and verifying if the function pointer is valid. If so, it can be executed as if it is a normal function:

Dim fp

On error resume next
Set fp = GetRef(KeyWord)
On error goto 0

If IsObject(fp) then
   TestResult = fp
Else
   MsgBox "Keyword '" & KeyWord & "' could NOT be mapped to a valid function."
End if


A function pointer can be used just as a normal function. A drawback of VBScript is the use of fixed arguments, but this can be solved as found elsewhere on teh internets: Passing an array or a dictionary object to the function.

Dim aryArguments
Call GetArguments(aryArguments) 'A function to set all arguments into an array
TestResult = fp(aryArguments) 'Pass the arguments to the function pointed to by the function pointer


And a quick example:

Public Function SumSquares(byRef aryArg)
  Dim i, b
  For i = lbound(anyArg) to ubound (aryArg)
     b = b + aryArg(i)^2
  Next i
  SumSquares = b
End Function

Public Function SqrtSquares(byRef aryArg)
  Dim i, b
  For i = lbound(anyArg) to ubound (aryArg)
     b = b + aryArg(i)^2
  Next i
  If b >= 0 Then SqrtSquares = Sqr(b)
End Function

Public Function HandleKeyword(byval keyWord, byRef aryArg)
  Dim fp

  On error resume next
  Set fp = GetRef(keyWord)
  On error goto 0

  If IsObject(fp) then
     HandleKeyword = fp(aryArg)
  Else
     MsgBox "Keyword '" & KeyWord & "' could NOT be mapped to a valid function."
   HandleKeyword = "INVALID KEYWORD"
  End if
End Function

Dim Arguments
Arguments = array(3, 4, 5)

MsgBox HandleKeyword("SumSquares", Arguments) ' -> shows "50"
MsgBox HandleKeyword("SqrtSquares", Arguments) ' -> shows ~ "7.07107"
MsgBox HandleKeyword("SumFactoerials", Arguments) ' -> shows "INVALID KEYWORD"