December 1, 2010

Using the Levenshtein Distance to get the best match from a list

A while ago, I created a document in which I explained how to use the Levenshtein Distance to get the best match from a list of options. Well, the article has its own introduction so if you are interested; start reading!

November 19, 2010

QTP 10 first impressions: Meh

This week, QTP 10 was installed at our workspace. We upgraded from 9.1 to 10, so we could also experience all new features of 9.5.
First of all, this is written from my own perspective. I don't use the object repository, the default report, multiple actions or the native QTP connection. I know, these things are improved, but I cannot write about them.

The good things
OK, the function viewer is great. Unfortunately, it does not display classes and properties, while functions and subs inside classes are displayed as Publics. What was the problem with displaying classes as expandable trees and with properties and methods underneath you would think.
Also functions starting with a square bracket [ are not rendered in the function viewer.

The Todo list could become quite handy, but after years working without it, I can... do without it.

The bad things
200 Megs. That is the amount of RAM QTP consumes if I start it, even without any scripts loaded. Just to be fair, I think it is more a problem with the installation and that only 2 of the 30(!!) patches are loaded by default, but I cannot change it and it bothers me. So I categorized it as a bad thing.
This brings me to the next point: Hangs. It hangs a lot and it makes my PC slow, especially Internet Explorer and Windows Explorer. Again, I think it is a problem with the installation and I have to look it up and/or ask support to our godlike and always kind administrators (without them I cannot do anything, so playing nice is important. Maybe they read this.). I think that is a bad thing.

The "Public Default Property Let Foobar()
End Property
End Property
End Property
End Property" syndrom.
WHY???

Variable typing is messed up:
Print "123" + "321" results in "123321". Are we switching to Javascript or what? I thought VBScript was proud of their dynamic var typing (as if that is a good thing). Don't mess with my brain by changing the working of the + sign. Make variables dynamic or make operators dynamic, not both!

Variable conversion is different:
I noticed a classical rounding error: subtracting two numbers can end up with a very small decimal part. Like (not a real example):
4.32 + -0.32 results in 4.00000000000014
Annoying when you want to compare two numbers during automated testing.

The bad things that remain
No autocomplete inside libraries.
No jump-to-function from libraries to other libraries.
The find is still buggy, with annoying "forward or backward" radiobutton.
'Sometimes a duplicate code line overwrites other code' bug.
No declaration check on variables other than during runtime.
Set myCompositeClass = [new componentA](new componentB) results in a syntax error while it is theoretical correct.

Conclusion
I think my installation is not correct, so I had a bad week and am a bit prejudiced about my new QTP. The function viewer certainly speeds up my work, unfortunately it is implemented poorly. The problems I have with my memory and performance is probably due to the installation, on my run PCs I do not have these problems, only on my development machine (but why couldn't it be correct the first time?). The changed variable conversion and operator functionality is really not a good thing. Besides that it influences our existing tests, it is not an improvement. The remaining annoyances stays, and from a user perspective this is merely a QTP 9.6 release and not a 10 major version. I think it is time HP/Mercury will get a real competitor and we get a real choice what automated test software we can use. They are now functioning like our dutch railway system: "Use our system or don't; we don't bother".

November 18, 2010

QTP and how to create an autodestruct class

Somewhere in our application under test, we have a webpage that only continues loading if the mouse is slightly moved. The page is loading while QTP is syncing the page. During the syncing, I cannot send mouse move commands from QTP to the application.

I don’t know why the application has this feature and “they” don’t want to fix it, so… work around time.


I created a low profile mousemove application in C that only does one thing: After 5 seconds it moves the mouse cursor one pixel to the right. And after 5 seconds it moves the cursor one pixel to the left. Repeat.


Actually it is an cut down version of out nolock.exe application, this application sniffs the mouse cursor movements. If it detects inactivity for 10 minutes, it moves the mouse cursor slightly in the same manner as discussed above. This program has a shortcut in the Windows Startup folder and prevent the screen from locking when automated tests are running.


But back to the autodestruct class. We want to have a “single line of code” to implement this in any function where we expect the screen to hang. This class starts the mouse moving as soon as it is created and stops the mouse moving when it is destroyed.


Private Const MOUSE_MOVE_APPL_NAME = “mouseMover.exe”

Private Const BINARIES_FOLDER = “C:\QTP_Framework\tooling\bin\”


Class cls_AutoDestructMouseMover

Private Sub class_initialize()

‘ Put the cursor somewhere to prevent it is on the extreme right position

call extern.SetCursorPos(100, 150)

‘ Run the external mouse move application

systemutil.Run BINARIES_FOLDER & _

MOUSE_MOVE_APPL_NAME, "", BINARIES_FOLDER

End Sub


Private sub class_terminate()

‘ Kill the mouse move application

systemUtil.CloseProcessByName MOUSE_MOVE_APPL_NAME

End Sub

End Class


And make a public accessor for the class:


Public function [new AutoDestructMouseMover]

Set [new AutoDestructMouseMover] = new cls_AutoDestructMouseMover

End Function


Now, we can implement the class whenever it is needed:


Public Function Example

‘ Create the object, make it move!

Dim autoDestructMouseMover : Set autoDestructMouseMover = [new AutoDestructMouseMover]

Call TimeConsumingFunction


‘ Destroy the object, let it stop!

Set autoDestructMouseMover = Nothing

End Function


But wait, we called it an “autodestruct” class, what about the autodestruct? Well, The last line of code is not necessary. When the Example function ends, it automatically destroys all variables and objectpointers with local scope. We can simply reduce the implementation of the autodestructor to one line of code (I know, I am cheating with the semicolon):


Public Function Example

‘ Create the object, make it move!

Dim autoDestructMouseMover : Set autoDestructMouseMover = [new AutoDestructMouseMover]

Call TimeConsumingFunction

End Function


And the autoDestructMouseMover object is automatically destroyed with the function clean up.


Nice. But are there more appliances for an autodestruct object? Oh, yes.

What do you think an autoDestructStackTracer?

Class cls_AutoDestructStackTracer

Private procedureName_

Public Sub Init(pName)

procedureName_ = pName

Print now & “ ADST Start “ & procedureName_

End Sub


Private sub class_terminate()

Print now & “ ADST Exit “ & procedureName_

End Sub

End Class


And make a public accessor for the class:


Public function [new AutoDestructStackTracer](pName)

Set [new AutoDestructStackTracer] = new cls_AutoDestructStackTracer

[new AutoDestructStackTracer].Init pName

End Function


And let’s test it:


Public Function ExampleParent

Dim autoDestructStackTracer : Set autoDestructStackTracer = [new AutoDestructStackTracer](“ExampleParent”)

Print “I’m with ExampleParent now!”

Call ExampleChild

End Function


Public Function ExampleChild

Dim autoDestructStackTracer : Set autoDestructStackTracer = [new AutoDestructStackTracer](“ExampleChild”)

Print “I’m with ExampleChild now!”

End Function


Call ExampleParent

Output:

18-11-2010 9:06:06 ADST Start ExampleParent

I’m with ExampleParent now!

18-11-2010 9:06:06 ADST Start ExampleChild

I’m with ExampleChild now!

18-11-2010 9:06:06 ADST Exit ExampleChild

18-11-2010 9:06:06 ADST Exit ExampleParent


And consider uses of an [new AutoDestructFunctionTimer](pName) to measure the performance of your different functions. Or a [new ScreenShotOnFunctionExit] (and more generalized: [new ExecuteOnFunctionExit](“call PerformScreenshot”)) when you have lots of functions with multiple exit points.

October 29, 2009

Book review Quicktest Professional Unplugged

Introduction
After unpacking the book, it smiled to me in a pretty color setting, although, it reminded me on my times working at ABN AMRO. The cover shows the well known picture of the QTP splash window. The book is quite large, approximately 20x30cm and contains around 430 pages. The pages are all printed in black and white (well, the white was not printed, it was more the property of the paper itself). It is build up into chapters, and the book does have a table of contents and an index.


Content
The first thing that you’ll notice is lots of sample code. This comes in two ways: Code that supports the text and code that belongs to the challenges at the end of each chapter. The book is mainly written in a the first person plural form, so you will never be addressed personally, making the book somewhat formal. Nevertheless, it is an easy read. The layout is open and organized. Each chapter handles one subject and is providing you an introduction to this subject justifying why it is included in the book. Then it hops directly to the matter. At the end of each chapter is room for your own notes.

Most of the subjects are not new, and can be found over the web, the QTP help file and documentation provided by Mercury/HP, but it is very helpful to have it packed in one complete book and only the –no need to search anymore- property makes it worthwhile owning this book alone.
When I have to cover a new subject during my daily work, I peek into the applicable chapter which brings me on track in no time. The text and the code sticks very to the matter, meaning it is all about solving low level problems you’ll encounter by coding your automated tests.

It does not describe test methodologies or how to build a test framework. Well, actually there is a chapter “Designing Frameworks”, but it is more a guideline and best practices enumeration. Also you’ll not find examples dealing the problems of a virtual donut shop or remote control.
Tarun Lalwani writes: “This book is targeted at automation engineers who want to exploit the power that QTP offers…” and I think that is a very good description of the book. The sample code is of high level, sometimes too extensive. Programming experience is necessary to extract the full potential of it.

Conclusion

QuickTest Professional Unplugged is a very comprehensive book written by experience. It is full of handy sample code and neat tricks. Each chapter will provide you with one or more “I didn’t know that!” experiences. The book is created for a small audience, but for this particular audience it is certainly a must have.

April 26, 2009

How to use classes in QTP revisited

Classes in QTP are a bit tricky if you want to use them for the first time. You have to know and understand the principle that a class got local scope for the QTP Function Library (.qfl file) it is in, and that if you want to use it in another library, you’ll have to create a constructor (I have shown this before, but I’ll do it again):


' Constructor:
Public Function [new CustomClass]()
     Set [new CustomClass] = new cls_CustomClass
End Function

' Class definition:
Class cls_CustomClass
     Private Sub Class_Initialize()
         Print "Custom Class: I am created!"
     End Sub
End Class

' using the object in another library:
Dim myClass
Set myClass = [new CustomClass]

This seems like a drawback, but it creates a great possibility. Normally, it is not possible to pass initialisation parameters into a new class, because Class_Initialize does not accept parameters. But using the constructor function, we can!

Option Explicit

' Make a private variable that we can pass into the class
Private PARAMETER_ARRAY

' Create a function that returns the requested class. Accepts
' a parameter array. Note: Do not use paramArray, it is reserved!
Public Function [new customClass](ByVal parameterArray)

     PARAMETER_ARRAY = parameterArray

     ' Return a clsCustomClass object
     Set [new CustomClass] = new cls_CustomClass

End Function

' Create the custom class. Classes are always declared Private in QTP
Class cls_CustomClass

     ' The sub Class_Initialize is used to initialize the object with the
     ' parameters passed into the constructor
     Private Sub Class_Initialize()
         ' Do something with the parameters
         Print join(PARAMETER_ARRAY, vbNewLine)
     End Sub

End Class

' Test code
Dim myClass
Set myClass = [new customClass](array("first parameter", "second parameter", "etc."))

Notice what we just did: We created a Private variable, that can only be used by CustomClass classes. So with the use of Private variables with local scope to the library where you put the class in, you create a static variable for the class!
A static variable in this context is a variable that keeps its value and can be used over multiple objects of the same class:

' Declaring the static variables
Private INSTANCE_COUNTER : INSTANCE_COUNTER = 0

' Constructor:
Public Function [new CustomClass]()
     Set [new CustomClass] = new cls_CustomClass
End Function

' Class definition:
Class cls_CustomClass
     Private Sub Class_Initialize()
         Print "I am created and I have " & INSTANCE_COUNTER & " sister(s)."
         INSTANCE_COUNTER = INSTANCE_COUNTER + 1
     End Sub

     Private Sub Class_Terminate()
         INSTANCE_COUNTER = INSTANCE_COUNTER – 1
     End Sub
End Class

' using the object in another library:
Dim a, b, c
Set a = [new CustomClass]
Set b = [new CustomClass]
Set c = [new CustomClass]

Results in:
I am created and I have 0 sister(s).
I am created and I have 1 sister(s).
I am created and I have 2 sister(s).

By using the static variable as a reference for the object itself, we can create a singleton object. A singleton is an object whereof only one instance at a time can exist. In object oriented languages, singletons are normally used for large objects, or objects that will go bad if multiple instances exists like deadlocks or instability.

Option explicit

Private SINGLETON : Set SINGLETON = Nothing

Public function [new Singleton]()
     If SINGLETON Is Nothing Then
         set SINGLETON = new cls_Singleton
     End If

     Set [new Singleton] = SINGLETON
End Function

Class cls_Singleton

     Private Sub Class_Initialize
         Print "I am unique!"
     End Sub

End Class

Dim st1, st2, st3
Set st1 = [new Singleton]
Set st2 = [new Singleton]
Set st3 = [new Singleton]

Results in only one:
I am unique!

There is a drawback: Once a singleton object is created this way, it is not possible to destroy it without the use of some code violating the object oriented principle.

April 24, 2009

QTP variable name conventions

When you start with a new test automation project, your code is conveniently arranged and you still have a clear overview over the locations and naming of variables and functions. But later on it will become a disaster if you don’t manage it a little bit.
That is why I wrote a name convention article. It is not an official how-you-should-do-it document, it is just the way I do things and written from experience.

' Public and Private constants in capitals
Public Const MOUSEEVENTTF _MOVE = 1
Private Const APPLICATION_MAIN_WINDOW = "name:=My Application"

' Functions, Subs and local variables in lowerCamelCase
Public Function myCustomFunction(thisVariable, thatVariable)

     ' Variables with a known type can be declared with the type abbreviation in front of it
     Dim arrStaticArry(5), objFile, intCounter, blnReadOnly

     ' Variables with a unknown type are declared without a type abbreviation, This is also applicable for variables used in self commentary code.
     Dim customContainer, fileWasFound

     ' Consts with local scope are declared with the same format as variables
     Const cannotBeChanged = True

End Function

' Classes in UpperCamelCase. In QTP I use the cls_ tag in front of it for reasons explained later.
Class cls_EventListener

     ' Public variables are in UpperCamelCase too
     Public BufferLength

     ' Private variables with class scope are lowerCamelCase with an underscore behind it
     Private updateCounter_, eventNumber_

     ' Properties, Subs and Functions (public and private) are all in UpperCamelCase
     Public Property Get EventNumber()
         EventNumber = eventNumber_
     End Property
End Class

In QTP, a class gets a local scope. To make it global, you have to add a function returning that class. As a side effect, this gives you the opportunity to initialize the class. Of course you have to add an init method to your class if you do it this way.

Public Function [new EventListener](initializationParameters)
     Set [new EventListener] = new cls_EventListener
     [new EventListener].Init(initializationParameters)
End Function

Now, you can set a new class with the following code in another library:
Set EventListener = [new EventListener]("codebase:=Unicode")

Side note: The square brackets around a variable lets you enter every character for a variable, including spaces, special characters etc. If you find that inconvenient, you can use an underscore: new_EventListener to mimic normal VB functionality.
I use the square brackets when I want to express importance for example:

' Call the main routine of this script:
[___ !MAIN! ___]

' Or to enjoy my co workers (and to see if they ever peer review my code):
Dim [ O\-<>-/O ], [ ¿Que? ]

April 8, 2009

A few ways to use arrays

When you first encounter Arrays in QTP it is not very easy to understand quickly. VBScript makes use of a few types of arrays: Static, Dynamic, Assigned to a normal variable and Dictionary objects (the dictionary object is not discussed in this article.). Static and Dynamic arrays can be one dimensional or multidimensional.

First the simple static array. Static because it can only contain a fixed amount of items; Simple because we only put strings in it referred by the indexnumber (or subscript) of the array:


' Simple static array
Dim weekdays(6)

weekdays(0) = "Mon"
weekdays(1) = "Tue"
weekdays(2) = "Wed"
weekdays(3) = "Thu"
weekdays(4) = "Fri"
weekdays(5) = "Sat"
weekdays(6) = "Sun"

When you don't have plans to use the subscript, you can also create the array directly. And yes, it works the same with index numbers as with the weekdays array, but it is good programming practice to use the former method if you want to call the array items by subscript number.

' Simple assigned array
Dim workdays
workdays = array("Mon", "Wed", "Thu", "Fri")

As you can see, the variable does not have to be declared as an array, so theoretically every variable can be set as an array just as every variable can be set as an object. Although, with arrays you don't need the set statement.

Sometimes it is more convenient to dynamically increase and decrease the array size. Then you have to create a dynamic array. This is an array declared the same way as in the simple static array, but without the number of elements, just empty parenthesis: dim myArray()
To set the amount of array items, you have to use ReDim like ReDim myArray(n) where n is the amount of items you want to use +1 (The first subscript is always 0). Other then in a static array declaration (like Dim myArray(5)), the number of subscripts in a ReDim statement can be a variable or constant.
Whenever you use ReDim, the array is reïnitialized, except when you use the Preserve command, indicating you want to reuse the already set values:

' Simple dynamic assigment of an array
Dim myPets()
ReDim myPets(2)
myPets(0) = "Dog"
myPets(1) = "Cat"
myPets(2) = "Hippopotamus"

ReDim Preserve myPets(3)
myPets(3) = "Rabbit"

msgbox "My pets: " & join(myPets, ", ")

The join(array[, separation character(s)]) function lets you easily merge an array to a string.
Another trick is to use join to make an html table easily:
newTableRow = "" & join(arrElements, "") & ""

Multidimensional arrays
A multidimensional array is used to store data or objects in a matrix. You can create virtually create as much dimensions if you want (not really unlimited of course, but keep in mind this good rule with programming: If you have to ask what the limit is for some kind of instance, probably there is something wrong with your design)


' Multidimensional static array, the next code is pure and alone for demonstration purposes
' it is not optimised, maybe even not correct and there are better ways to achieve this
Dim bcCalendar(2100, 12, 31)
Dim dayCounter, maxDay, cYear, cMonth, cDay
dayCounter = 6

For cYear = 0 to 2100
    For cMonth = 1 to 12
        Select Case cMonth
            Case 1,3,5,7,8,10,12    maxDay = 31
            Case 4,6,9,11           maxDay = 30
            Case 2                  maxDay = 28 + abs((cYear mod 400 = 0) or ((cYear mod 4 = 0) and not (cYear mod 100 = 0)))
        End Select

        For cDay = 1 to maxDay
            bcCalendar(cYear, cMonth, cDay) = weekday((dayCounter mod 7)+1)
            dayCounter = dayCounter + 1
        Next
    Next
Next

msgbox "Charles Darwin was born on a " & WeekdayName(bcCalendar(1809, 2, 12))



Passing arrays
Arrays are passed the same way as variables are:

Default: By Reference
With ByRef in the function declaration: By Reference
With ByVal in the function declaration: By Value
With parenthesis around the argument in the function call: always By Value (ByRef in the function declaration is omitted)

Arrays filled with objects
Arrays do not only have to contain variables, they can also contain objects, which is great to make a collection of QTP gui objects, dictionaries, but also class objects to create child classes under a parent.
Arrays can even contain other arrays. This is useful if you want to create a fully dynamic multidimensional array. (Normally, in a multidimensional array, only the last item is expandable with a redim preserve statement.)