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"

6 comments:

Anonymous said...

Nice use of Function Pointers....

Neelkanth said...

Good post..
Thanks a lot....

Anonymous said...

when you are doing
Set fp = GetRef(KeyWord)

the function Named KeyWord is getting executed as if it is called.

I tested it by creating a function with a msgbox statement in it and named it KeyWord.

Let us know if you can think of a solution...

~neethu

Anonymous said...

Now i tried this, the behavior is even strange.


Dim fp
Dim a

a=0

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

msgbox "fun not called yet"

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

Function KeyWord
a=a+5+4
end Function

Bas M. Dam said...

What behaviour is strange? What did you expect and what was the result? (We are testers, remember! be specific and complete.)

Try this (untested, I do not have QTP on hand right now):

Option Explicit
Dim fp, a, KeyWord

a=33
KeyWord = "Sum9"

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

msgbox a ' returns "33"
msgbox "fun not called yet"

If IsObject(fp) then
msgbox fp(a) ' returns "42", the result of function Sum9 injected with 33
Else
MsgBox "Keyword '" & KeyWord & "' could NOT be mapped to a valid function."
ExitTest
End if
msgbox a ' returns "33" (a is unchanged)

a = fp(a)
msgbox a ' returns "42"

Function Sum9(inVal)
Sum9 = inVal + 9
end Function

What you was doing: you created a function pointer to an string that was the result of function KeyWord. That went wrong presumably (you can check that by commenting the on error resume next statement), causing a cascade of strange behaviour, for example the result 9 in the first msgbox, while 0 was expected.

White Card Training said...

Now I know how, thank you for this post!