Login   /   Register

Limitations of RegisterUserFunc

Rate this article
     5 votes, average: 3.2 out of 55 votes, average: 3.2 out of 55 votes, average: 3.2 out of 55 votes, average: 3.2 out of 55 votes, average: 3.2 out of 5
Loading ... Loading ...
February 10th, 2009 by Meir Bar-Tal

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

10 Responses to “Limitations of RegisterUserFunc”

  1. mister.Chipmunk Says:

    [+]

    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... ...

  2. Meir Bar-Tal Says:

    [+]

    You raised 2 points. I will answer both. 1) Quote: "to provide possibility to use native qtp objects methods inside these functio... ...

  3. mister.Chipmunk Says:

    [+]

    Thank You for your answer. Initially, there is only one thesis, because 2 points, which You devided from my comment are closely re... ...

  4. Meir Bar-Tal Says:

    [+]

    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 ... ...

  5. pmgrossman Says:

    [+]

    Hey Mier, Great article. You and mister chipmonk both have good points, but I think we are missing the big picture: The single-... ...

  6. Meir Bar-Tal Says:

    [+]

    Hey Paul, Thanks for your enlightening comment. My comments: 1) Regarding recursion, it's obvious that HP (Mercury) intended to l... ...

  7. pmgrossman Says:

    [+]

    Meir OK. I understand your point and agree. And I probably have made a mistake putting this at Mercury's door. I would submit t... ...

  8. Meir Bar-Tal Says:

    [+]

    The below explanation is given in general terms to make the point (some subtleties might be ignored). Well, the concept comes f... ...

  9. papu4 Says:

    [-]

    Great work, Thanks a lot..

  10. rajkanwarsingh Says:

    [+]

    Hi Meir, I was just trying these limitations and one thing that i found was that with the First limitation that says that the ... ...

Leave a Reply

You must be logged in to post a comment.

This article was viewed 828 times