December 13, 2011

QTP and Method Chaining disclosed

With method chaining, you can chain object methods to an object in order to change it. It is the default paradigm of JQuery. After using it a while with JQuery, I figured out it is an extremely usefull method to use with QTP.

If you use QTP long enough, you start to create your own function. But because VBScript doesn't allow optional parameters in homebrew functions, existing functions become clumsy with all types of parameters you don't use most of the time.
I remember using functions like findTextInTable(oTbl, txtToFind, startRow, endRow, startColumn, endColumn, byRef foundRow, byRef foundColumn) where each parameter could also be an array or contain signal characters. This results in a function call like this:
findTextInTable(myTable, array("account xyz", "!customer \d+"), 4, "", "", 5, foundRow, "")
So far for self-explanatory code.

When you use object chaining, you use only the parameters you need AND your code will become self explanatory. The code above will become:
xTable.addFindText("account xyz").addRegexFindText("customer \d+").startRow(4).endColumn(5).getFindRow(foundRow)

Of course this need some programming skills to build this correctly into a class, so let's start with an easy example: A string wrapper. When we create the class for the stringwrapper, we use functions for the methods. Functions can return primitives or objects, and with each object call, we return the object itself. You can also use "Get Property", but that will restrict you in the amount of parameters you can pass with your method to be exactly the same as the Let/Set Property.

Class cls_xString

    Private value_

    Public Function SetValue(v)
        value_ = v
        Set SetValue = me
    End Function

    Public Default Function getValue()
        getValue = value_
    End Function

    Public Function ToUpper()
        value_ = uCase(value_)
        Set ToUpper = me
    End Function

    Public Function Append(v)
         value_ = value_ & v
        Set Append = me
    End Function

    Public Function Prepend(v)
        value_ = v & value_
        Set Prepend = me
    End Function

    Public Function Surround(v)
        value_ = v & value_ & v
        Set Surround = me
    End Function

End Class

We use another trick with the getValue; it is the default return value if the string class is called without a Set. To make the class usable globally, we need an initiator function that returns an object of class xString:

Public Function [new xString](initialValue)
    Set [new xString] = new cls_xString
    call [new xString].SetValue(initialValue)
End Function

And now we can create some testcode:
Dim myString
Set myString = [new xString]("W00t, Method Chaining is really Working!")
msgbox myString.ToUpper.Prepend("(").Append(")").Surround("'")

This will result in an editbox with text '(W00T, METHOD CHAINING IS REALLY WORKING!)'
The method are parsed from left to right; first the string is transformed to uppercase, then prepended by (, appended by ) and finally surrounded by '.

Other usage:
- Narrowing down a collection and get a single object
Set linkCollection = [new linkCollection](oPage)    ' Get all links from a page
Set myLink = linkCollection.withRegexText(".*Help.*").Index(3)

Or do directly an action on that object:
linkCollection.withRegexText(".*Help.*").Index(3).Click

- Narrowing down collections and do actions on them:
Set editCollection = [new editboxCollection](oPage)    ' Get all editboxes from a page
' Set all editboxes that are visible, enabled and empty with a value
editCollection.withProperty("visible:=true").withProperty("enabled:=true").withValue("").setValue("Foo Bar!")

- Do a custom action on an object
Private Function DisplayToString(object)
    MsgBox object.ToString()
End Function
Set editCollection = [new editboxCollection](oPage)    ' Get all editboxes from a page
' Show the toString value of all editboxes that are not visible on a page
editCollection.withProperty("visible:=false").each(getRef("DisplayToString"))

Method chaining is very versatile, with the trap of being used to much. Actually it does pretty much the same using the 'with' keyword with objects. It can make your code more clear, or just more obfuscated. Use it wisely.