5.20. How to Modify Squish Functions

In some situations it is useful to modify Squish functions—for example, to record every call to a particular function in the test log. Some scripting languages support the replacing of one function with another of the same name, so we can take advantage of this facility in Squish. Keep in mind though, that most of Squish's functions are not accessible until after a call to the startApplication function, so any modifications we want to do must be done after that call.

Suppose, for example, that we want to change the clickButton function so that it writes to the test log every time it is called. Here's how it can be done:

Python


import sys # Add this line

def wrapSquishFunction(functionName, wrappedFunctionGetter): # Add this function
    module = sys.modules["squish"]
    if functionName in dir(module):
        wrappedFunction = wrappedFunctionGetter(getattr(module, functionName))
        setattr(module, functionName, wrappedFunction)
    else:
        raise RuntimeError("function %s not part of squish module" % functionName)

    if functionName in globals():
        globals()[functionName] = wrappedFunction

def addLoggingToClickButton(clickButtonFunction): # Add this function
    def wrappedFunction(button, logText="clickButton() called"):
        test.log(logText, str(button))
        clickButtonFunction(button)
    return wrappedFunction

def main():
    startApplication("addressbook")
    wrapSquishFunction("clickButton", addLoggingToClickButton) # Add this line
    # ...

JavaScript

function addLoggingToClickButton(clickButtonFunction) // Add this function
{
    return function(button, logText) {
        if (!logText)
            logText = "clickButton() called";
        test.log(logText, button);
        clickButtonFunction(button);
    }
}

function main()
{
    startApplication("addressbook");
    clickButton = addLoggingToClickButton(clickButton) // Add this line
    // ...

This creates a custom clickButton function that has a different API from Squish's built-in clickButton function in that the custom function can accept an additional optional argument. This means that existing calls will continue to work as normal (only now each call will result in a test log entry), and that we can add additional information to any calls to the clickButton function as we like. For example, we could replace, say:

clickButton(waitForObject(names.address_Book_Add_OK_QPushButton));

with:

clickButton(waitForObject(names.address_Book_Add_OK_QPushButton),
        "Add Requested");

If we make this change the Test Results will list a Log entry with the Message text “Add Requested”, and with a Location of the file and line number where the call was made. And for all calls of the clickButton function that are left unchanged each one will result in a Log entry with a Message text of “clickButton() called” since that's the default text we have used in the addLoggingToClickButton function.

Naturally, the addLoggingToClickButton function could be put in a shared script if you wanted to use it in several different test cases. And it should be straightforward to use the addLoggingToClickButton function as a model for wrapping other Squish functions, either to add logging, or to make them do other things before or after calling the original function—or both before and after.

If you want to keep access to the original function, then just before the call to the addLoggingToClickButton function, put a line such as originalClickButton = clickButton. Now, whenever you want to use the original function use originalClickButton.

[Note]Perl, Ruby, and Tcl-specific

We do not have working Perl, Ruby, or Tcl equivalents of the addLoggingToClickButton function although it should be achievable in these languages (probably with some rather subtle code for Perl and Ruby, and with quite a bit of code for Tcl).

[Note]Python-specific

If we are willing to assume that Squish will always use positional rather than keyword arguments (or are willing to take the chance), we can make a single generic wrapping function that can be used to add logging to any Squish Python function rather than having to use one wrapping function per function to be wrapped.

Here's the function and how we'd register it for use:

Python


import sys # Add this line

def wrapSquishFunction(functionName, wrappedFunctionGetter): # Add this function
    module = sys.modules["squish"]
    if functionName in dir(module):
        wrappedFunction = wrappedFunctionGetter(getattr(module, functionName))
        setattr(module, functionName, wrappedFunction)
    else:
        raise RuntimeError("function %s not part of squish module" % functionName)

    if functionName in globals():
        globals()[functionName] = wrappedFunction

def addLogging(function): # Add this function
    def wrappedFunction(*args, **kwargs):
        if "logText" in kwargs:
            logText = kwargs["logText"]
        else:
            logText = "Logged function called"
        arg0 = ""
        if args:
            args0 = args[0]
        test.log(logText, str(arg0))
        function(*args)
    return wrappedFunction

def main():
    startApplication("addressbook")
    wrapSquishFunction("clickButton", addLogging) # Add this line
    # ...

As before the clickButton function will behave normally, but now it will accept an optional keyword argument called logText. So now, for example, we could replace, say:

clickButton(waitForObject(names.address_Book_Add_OK_QPushButton));

with:

clickButton(waitForObject(names.address_Book_Add_OK_QPushButton),
        logText="Add Requested");

The advantage of this more generic wrapper is that it can be used on any Squish function, even those that take more than one positional argument.

See also, FAQ, Q: 10.1.1.