In previous articles (e.g., A Fresh Look on RegisterUserFunc, Assa, 2008) we have discussed one of the most powerful features provided by QuickTest Professional (QTP): "the ability to override methods of [test] object classes with custom functions tailored to serve specific purposes" (Bar-Tal, 2008b). Whilst the advantages of this feature were already reviewed in the articles mentioned and quoted above, some limitations are seemingly undocumented in the QuickTest reference. This brief article will describe two limitations of this mechanism as emerged from an extensive research I conducted recently.
Limitation 1: Number of Function Arguments
When defining a function that overrides a method, it must have the same signature. This means that the overriding function cannot have a number of arguments different than the overridden method. The snippet below shows examples of correct and incorrect versions of overriding functions
'Native method signature
Function Exist(ByRef obj, ByVal intSeconds)
'Example 1: Overriding (registered) function (correct)
Function ExistEx(ByRef obj, ByVal intSeconds)
'Example 2: Overriding (registered) function (correct)
Function ExistEx(ByRef obj, ByVal objDictionary)
'Example 3: Overriding (registered) function (incorrect)
Function ExistEx(ByRef obj, ByVal intSeconds, ByVal strRetries)
'Example 4: Overriding (registered) function (incorrect)
Function ExistEx(ByRef obj)
Snippet 1. A native method (Exist) and its respective correct/incorrect custom overriding function signatures
One possible workaround is to pass as one of the function arguments a Dictionary or an Array (a dictionary is preferable to avoid argument order mistakes). However, this is impractical in the case of a method that accepts only a test object as the only argument (for example, the Browser Sync method), since the syntax of the automation model requires one to use the dot operator (e.g., Browser("GMail").Sync) to get access to the test object’s methods, so there is no room for the additional argument or for wrapping the object together with additional arguments with a Dictionary.
One possible reason for this limitation might be that as the RegisterUserFunc is aimed at enabling one to change the basic method functionality offered by QTP, it was intended only to enable internal functional modifications without allowing for interface changes (i.e., the function signature). This limitation can also be viewed as an advantage, because it constrains the changes allowed for infrastructure methods. This may allow for code that is more portable between projects and less complex as the interface remains fixed. However, I do not share this view, since it does not allow for a straightforward implementation of method overloading, which is the way true OO languages enable polymorphism, which is a powerful technique (in a future article I will show how this can be accomplished despite the limitations outlined here).
Limitation 2: Registered Functions Interoperability
When a registered function includes a call to another registered function, care must be taken about the correct syntax. While usually one would put a Call obj.[native method]([arg1], […], [argn]) to call a registered function (such that no changes to existing calls in your code should be done), you might get a VB Script Runtime Error: "Type Mismatch" during your run session. It seems that the mechanism has an inherent limitation (perhaps it is a bug?) as it is unable to trace the call from a registered function through the object class to another registered function. The only solution I found in such a case is to write the code in a "scripting" (rather than OO) fashion, passing the test object as the first argument, as follows: Call [custom method](obj, [arg1], […], [argn]). See a real life example in the following snippet.
Public Function IsEnabled(ByRef obj, ByVal intTimeoutMSec)
IsEnabled = obj.WaitProperty("disabled", 0, intTimeoutMSec)
End Function
Public Function WaitPropertyEx(ByRef obj, ByVal strPName, ByVal varPValue, ByVal intTimeoutMSec)
If obj.Exist(0) Then
WaitPropertyEx = obj.WaitProperty(strPName, varPValue, intTimeoutMSec)
Else
WaitPropertyEx = False
End If
End Function
Public Function SetEx(ByRef obj, ByVal strValue)
If obj.IsEnabled(Environment("default_timeout_msec")) Then
obj.Set strValue
SetEx = True
Else
Reporter.ReportEvent micWarning, "Set", "Object is disabled"
SetEx = False
End If
End Function
RegisterUserFunc "JavaEdit", "IsEnabled", "IsEnabled"
RegisterUserFunc "JavaEdit", "WaitProperty", "WaitPropertyEx"
RegisterUserFunc "JavaEdit", "Set", "SetEx"
Snippet 2. Three interdependent registered functions (leading to runtime error)
The snippet shows three registered functions. Two of them are overriding versions of the QTP native methods (Set and WaitProperty) and one is an extension of the object’s functionality that checks if the object is enabled with a timeout. The overriding functions help us catch situations that are frequently found during a run session, while the extension function wraps a frequently used call to the WaitProperty function, one that helps the script better synchronize. For example, such synchronization might be crucial in case the Edit control changes its state from disabled to enabled following a GUI operation like marking or unmarking a checkbox.
The sequence of calls between the above functions is as follows. After the registration, when we call the obj.Set method, actually the SetEx function will be called. The SetEx method will then first check if the object is enabled, obviously by calling the IsEnabled extension method, just before actually attempting to change the value of the Edit control. The IsEnabled function, in turn, will call the object’s WaitProperty method and, similar to the case of the Set function, it will actually call the registered WaitPropertyEx function.
This seems pretty logical and one might not suspect that there is some flaw in this design. However, my experience has shown that attempting to implement such a scheme leads to a runtime error (Type Mismatch). Extensive fiddling with such functions led me to the conclusion that QTP is unable to trace correctly the sequence of calls when one registered function calls another.
There is, however, a way to make such functions interoperability actually work. The (awkward) solution is shown below.
Public Function IsEnabled(ByRef obj, ByVal intTimeoutMSec)
IsEnabled = WaitPropertyEx(obj, "disabled", 0, intTimeoutMSec)
End Function
Public Function WaitPropertyEx(ByRef obj, ByVal strPName, ByVal varPValue, ByVal intTimeoutMSec)
If obj.Exist(0) Then
WaitPropertyEx = obj.WaitProperty(strPName, varPValue, intTimeoutMSec)
Else
WaitPropertyEx = False
End If
End Function
Public Function SetEx(ByRef obj, ByVal strValue)
If IsEnabled(obj, Environment("default_timeout_msec")) Then
obj.Set strValue
SetEx = True
Else
Reporter.ReportEvent micWarning, "Set", "Object is disabled"
SetEx = False
End If
End Function
Snippet 3. Three interdependent registered functions (working version)
As is shown in Snippet 3 above, changing the calls to other registered function such that the overriding functions are explicitly called does the trick. This way we can enjoy of both worlds: enjoy the OO-like coding style by registering the functions as usual, and also enjoy full interoperability among the registered functions.
Though I have no inside knowledge of the way the HP (Mercury) people have implemented the function registration mechanism, I will attempt to give my black-box tentative guesses as to the root cause of this limitation. My analysis suggests that the registration mechanism seems to hold a reference list that indicates which function should be called instead of the native method, but this list can be used only to one level depth. In other words, after reaching the scope of an overriding function, the registration retrieval mechanism either stops checking for such references until exiting the function or has some trouble with the cross-reference. I assume that HP (Mercury) had no intention for, or did not foresee, such complex use in practice. It is my view that originally, the basic functionality of the function registration mechanism only aimed at enabling developers to add custom functionality to the basic methods. For example, this mechanism is useful to enhance the control over the test flow with custom error handling and verifications, as well as with custom reporting. However, it may well be that the behavior of the registration mechanism was not designed (and hence also not tested) with regard to the interoperability issue described above.
Summary
This short article reviewed two apparent limitations of QuickTest Professional’s function registration mechanism. It was indicated that an overriding function must have the same signature (number of arguments) as the native method, and that the implementation of overridden functions interoperability requires a special treatment to make it work. In a future article I plan to provide a workaround for the first limitation to enable the implementation of a (true) kind of polymorphism. As to the second limitation, a simple workaround was suggested that just changes the code writing style from OO-like to scripting-like.
Comments, questions and suggestions can be posted here below or sent to: meir.bar-tal@advancedqtp.com
Posted in Meir Bar-Tal's Blog, QTP Hacks


Meir Bar-Tal




February 10th, 2009 at 6:53 pm
Thank You very much for such detailed article. The first limitation is new fo me.
As for the second one, I think, the main reason, why any functions, registered through RegisterUserFunc mechanism, can’t be used inside other custom registered functions is to provide possibility to use native qtp objects methods inside these functions, which is very useful.
There is no way to distinguish, which method: native or overridden - You want to use inside registered function,, if You use OO code style, so calls of other registered functions are blocked from these functions.
February 10th, 2009 at 9:33 pm
You raised 2 points. I will answer both.
1) Quote: “to provide possibility to use native qtp objects methods inside these functions, which is very useful.”
- With all due respect, I don’t think this adds anything new to the discussion.
2) Quote: “There is no way to distinguish, which method: native or overridden - You want to use inside registered function”
- Why not? We know that QTP is unable to follow the chain to look-up and call the appropriate function. But there is no a priori evidence for this - which means that I could not hypothesize that this would be the case in the first place. My conclusion in the article was less ambitious: I only pointed to what seems to be an inherent limitation in the mechanism as HP (Mercury) have built it.
February 11th, 2009 at 1:16 pm
Thank You for your answer. Initially, there is only one thesis, because 2 points, which You devided from my comment are closely related.
I’ll give an example, based on code, demonstrated in the article.
Imagine, we have two overriden methods “SetEx” and “WaitPropertyEx” for the same class of objects - “JavaEdit”:
[code]
Public Function WaitPropertyEx(ByRef obj, ByVal strPName, ByVal varPValue, ByVal intTimeoutMSec)
If obj.Exist(0) Then
WaitPropertyEx = obj.WaitProperty(strPName, varPValue, intTimeoutMSec)
Else
WaitPropertyEx = False
End If
End Function
RegisterUserFunc “JavaEdit”, “WaitProperty”, “WaitPropertyEx”
[/code]
And I’ll slightly change “SetEx” function from the same function, mentioned in article:
[code]
Public Function SetEx(ByRef obj, ByVal strValue)
If obj.WaitProperty(”disabled”, 0, intTimeoutMSec) Then
obj.Set strValue
SetEx = True
End If
End Function
RegisterUserFunc “JavaEdit”, “Set”, “SetEx”
[/code]
My point is that in condition “If obj.WaitProperty(”disabled”, 0, intTimeoutMSec) Then” there is no way for QTP to distinguish native or overriden method we want to use, because from the one side we can use native methods of objects inside registered functions, but from the other side “WaitProperty” method for objects of “JavaEdit” calss is overriden by “WaitPropertyEx” functions. Due to this dilemma, I think, objects send by reference to registered through RegisterUserFunc functions have only their native qtp methods and properties without further inheritance of additional and overriden methods.
February 11th, 2009 at 1:43 pm
Thanks for your reply.
I really don’t get your point. As I said, there is no a priori evidence for this dilemma. The basic logic should be that no matter where, check if the called native method is overridden by another function. I don’t see why this could not IN PRINCIPLE be implemented. Again, I only indicated that it is not workable IN PRACTICE.
Cheers! :)
February 11th, 2009 at 9:56 pm
Hey Mier,
Great article. You and mister chipmonk both have good points, but I think we are missing the big picture: The single-level implementation is intentional to make RAM gobbling recursion less likely when overridding a native method.
Your SetEx example overrides the intrinsic .Set method, but calls the native method internally. If the internal call executed your function method again, you would need even more code to implement a recursive exit point, right?
Mercury would not want to make it that easy for newbies to write a recursive function by accident, and we were all newbies once.
February 12th, 2009 at 9:22 am
Hey Paul,
Thanks for your enlightening comment.
My comments:
1) Regarding recursion, it’s obvious that HP (Mercury) intended to limit the newbies - obviously as they didn’t think of QTP users as real programmers However, you can find an example of such intrinsic recursion in my article “Override the Object Exist Property”. (I must be fair at this point and disclose the fact that I’m programming since 1987 as strong amateur and later for academic research and independent business, so I may not be the “classic” QTP user.)
2) Please correct me if I’m wrong, but from the comments I conclude that the point I think you and Mister Chipmonk have missed - which is the main issue I intended to bring to your attention - is that the mechanism does not IN PRACTICE allow you to call OTHER overridden methods, not themselves (but see the previous comment) - in which case no danger of recursion is inherently evident.
I’d be delighted to have your opinion on these points.
Cheers,
Meir
February 12th, 2009 at 1:00 pm
Meir
OK. I understand your point and agree. And I probably have made a mistake putting this at Mercury’s door. I would submit that Microsoft has always treated VBScript as the “Red-headed step child” of a programming language.
It’s user defined functions do not support optional parameters, unlike it’s intrisic functions like msgbox. It’s RegEx engine does not support lazy quantifiers. Even it’s ON ERROR implementation and Err object description is completly sub par by any standard. I bet the list goes on. So why would this be any different?
I have two more questions: Can you give example of a language that implements method override as you describe, and how does it allow you to determine the method, intrinsic or overriden, to execute?
February 12th, 2009 at 1:23 pm
The below explanation is given in general terms to make the point (some subtleties might be ignored).
Well, the concept comes from OO languages like C++, where the themes of encapsulation, inheritance and polymorphism can be implemented fully.
1) An inheriting (descendant) class can override a method of the base class (parent). The new method has the same name and can be invoked within the scope of an instance of either class. The actual behavior in each case will rely on the specific implementation. Moreover, as a result of the inheritance mechanism, any method implemented at base class level will be available at the descendant class level. This is what I call “overriding”.
2) A method can be implemented simultaneously with different signatures. At runtime the correct version is called based upon a match between the arguments supplied and the function prototype (signature). This has been coined as “polymorphism”.
I hope this covers your two questions, albeit somewhat superficially.
February 28th, 2009 at 2:51 pm
Great work, Thanks a lot..
November 2nd, 2009 at 10:18 am
Hi Meir,
I was just trying these limitations and one thing that i found was that with the First limitation that says that the overridden function must have the same signature as the native one — there is a some variation. As an Example that u have given as incorrect actually works:
‘Example 4: Overriding (registered) function (incorrect)Function ExistEx(ByRef obj)
I have tried the same with other methods too like Set, SetToProperty for Webedit and came to a conclusion that though u can’t have number of arguments greater than the native function but certainly your code will work even if you specify arguments less than that of native function. The Following Ex works fine (SetToProperty native function has three arguments)
RegisterUserFunc “WebEdit”, “SetToProperty”, “SetToPropertyEx”
Function SetToPropertyEx(ObjEdit, Property)
‘Body
End Function
Browser(”Browser”).Page(”Page”).WebEdit(”WebEdit”).SetToProperty “Prop”
Please give in your feedback/comments on the same
Thanks..
Raj