Featured

QTP 10 not Recognizing Oracle Forms Obje

Bidun Meduza shared on our Facebook page a problem and solution that I thought might be useful to you too. Problem He had a situation where QTP 10 was unable to recognize objects of Oracle Forms. Th

Read More
QTP 10 not Recognizing Oracle Forms Objects

New Navigation Menus

In order to ease orientation through our vast knowledge archive, we have added the following navigation menus: First Steps Scripting QTP VBScript Tutorial Knowledge Articles (by levels of expertise) B

Read More
New Navigation Menus

Permalinks

We’ve changed the permalinks format, so if you have been experiencing some trouble with finding materials with the old links, then remove the YYYY/MM/ from the URL and you’d be fine. In ca

Read More
Permalinks

Advanced QTP on Android

A first version of AdvancedQTP for Android can be found here. Now you can take this perfect companion with you to keep updated on what’s new in our site.

Read More
Advanced QTP on Android

New Advanced QTP Features: Layout and FB

As you have most surely noticed, we’ve recently changed the site’s theme in order to enhance the overall user experience. As from today, you can also register and login seamlessly with you

Read More
New Advanced QTP Features: Layout and FB Integration

Implementing a Generic Iterator with Function Pointers

0
by on January 19, 2009 at 11:00

 

Abstract

This article describes a powerful technique that exploits my previously outlined innovation – Function Pointers in VB Script – to build a generic mechanism having the capacity of executing components repeatedly: the generic iterator.

 

Introduction

Executing processes repeatedly is a common theme in software development, one that is at the core of data processing in IT as well as other domains (RT, etc.). Automation developers also have the need to implement iteration code structures (ubiquitously called loops) in their scripts and functions, and this is not surprising because “… testing automation … is, indeed, a specific kind of software development …” (Implementing a GUI Layer with Classes, Bar-Tal, 2008), though many times I have encountered QA managers who count automation as a testing activity. For example, iterations are used for table data verification (GUI level), file processing, and for data-driving tests (i.e., executing the same test flow with different data values). Moreover, the ability to execute a previously coded procedure (or function) repeatedly is at the heart of automation; it is exactly what makes automation so appealing as a true alternative to putting a human tester to perform the tedious task of filling forms, pressing buttons and verifying results hundreds and even thousands of times. All the more, since such tasks are also repeated periodically – each firm having its own application version release policy (quarterly, semi-annually, and even weekly and bi-weekly schedule). Though basically such iteration structures are not difficult to implement, they are a major source of defects found in the code produced by developers. One central reason for this are faulty loop exiting conditions, which not always are well defined. Consequently, it would be of great importance to have a method of implementation that eliminates the need for coding the iteration structure again and again.

In previous articles we have seen that it is possible to exploit Object Oriented Design Patterns in order to improve the code used in our automated tests with regard to maintainability, readability, scalability, extensibility, reusability and testability. Moreover, it has been argued that using Function Pointers can help us to improve the performance of our code, and that this improvement correlates positively with the number of times a function is called (Bar-Tal, 2008). Combined with the fact that with other (OO) languages professionals define and implement iterator interfaces successfully and ubiquitously, all the above suggest that developing such an interface would most probably be worthwhile. As aforementioned, the method outlined here below  is based on the basic techniques described in the article Function Pointers in VB Script (revised) (Bar-Tal, 2007, 2008). Hence that, before continuing, it is strongly recommended that the reader makes sure the ideas of function pointers and design patterns are well understood.

In this article I will propose a generic mechanism to handle iterations of executable code. I will attempt to show that using the method suggested here can have a beneficial impact on the efforts invested in development, testing, debugging and fixing defects, as it offers a service that can be used with ease. All it requires is the implementation of some very simple interfaces, as we will show in the next section.

Before delving into the actual implementation, let us define some terms. A generic iterator is a device capable of executing repeatedly a functionally independent executable component that performs a process upon a given object (which can be any data entity, such as a number or a mobile phone call data record). Since iterations are meaningful with variable inputs, it should be clear that the given process would act upon a different object in each iteration. Hence, the iterator would require at least two arguments: a collection of objects (operands) and a process (the operator). A process can be as simple as an arithmetic operation involving two numbers, but it can also be complicated as the calculation of a mobile customer bill, and more. Within the scope of the framework described hereby, the executable component that implements a process is built using the Command Wrapper design pattern. Functionally independent means that the specific blocks of code that we need to execute repeatedly are built without referencing any internals of the iterator device. In other words, the generic iterator does not care at all about the specific content included in the executable it activates, and the executable component does not need to take into account how the iterator that launches it is internally built. The only point in which both sides must be compatible is – as would be expected – with regard to their interfaces.

 

Method

In order to build the generic iteration mechanism we need to define the entities that will be involved in our implementation.  Three entities must be defined:

  • The Iterator;
  • The required process or operation (packed using the Command Wrapper design pattern);
  • The object (class instance) upon which the process or operation should be applied.

Let us define the Iterator class as follows:

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Class</span> Iterator

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span> <span style="color: #008000">\'Class: Iterator</span>

<span style="color: #606060">   5:</span> <span style="color: #008000">\'Encapsulates the Iterator Object</span>

<span style="color: #606060">   6:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">   7:</span> <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">   8:</span> <span style="color: #008000">\'    N/A</span>

<span style="color: #606060">   9:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">  10:</span> <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  11:</span> <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks Ltd.</span>

<span style="color: #606060">  12:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">  13:</span> <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  14:</span> <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  15:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">  16:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  17:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  18:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Default</span> <span style="color: #0000ff">Function</span> Run(<span style="color: #0000ff">ByRef</span> objCollection, <span style="color: #0000ff">ByRef</span> ptrFunction, <span style="color: #0000ff">ByVal</span> strExitCondition)

<span style="color: #606060">  19:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  20:</span>     <span style="color: #008000">\'Function: Run</span>

<span style="color: #606060">  21:</span>     <span style="color: #008000">\'Performs n iterations of ptrFunction on the objCollection items where n=objCollection.count.</span>

<span style="color: #606060">  22:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  23:</span>     <span style="color: #008000">\'If the strExitCondition holds true, the next iteration is not performed and the function is exited.</span>

<span style="color: #606060">  24:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  25:</span>     <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">  26:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  27:</span>     <span style="color: #008000">\'Arguments:</span>

<span style="color: #606060">  28:</span>     <span style="color: #008000">\'    ByRef objCollection - As Scripting.Dictionary</span>

<span style="color: #606060">  29:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  30:</span>     <span style="color: #008000">\'    ByRef ptrFunction - As Function Pointer (to implement callback)</span>

<span style="color: #606060">  31:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  32:</span>     <span style="color: #008000">\'    ByVal strCondition - String with condition to be evaluated.</span>

<span style="color: #606060">  33:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  34:</span>     <span style="color: #008000">\'Returns:</span>

<span style="color: #606060">  35:</span>     <span style="color: #008000">\'    Scripting.Dictionary with the results of each iteration</span>

<span style="color: #606060">  36:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  37:</span>     <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  38:</span>     <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks</span>

<span style="color: #606060">  39:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  40:</span>     <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  41:</span>     <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  42:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  43:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  44:</span>         <span style="color: #0000ff">Dim</span> count, items, ix, str, dicResults

<span style="color: #606060">  45:</span>         

<span style="color: #606060">  46:</span>         <span style="color: #008000">\'Create a Dictionary to store the results for each iteration</span>

<span style="color: #606060">  47:</span>         <span style="color: #0000ff">Set</span> dicResults = CreateObject(<span style="color: #006080">"Scripting.Dictionary"</span>)

<span style="color: #606060">  48:</span>         

<span style="color: #606060">  49:</span>         <span style="color: #008000">\'Get the collection count</span>

<span style="color: #606060">  50:</span>         count = objCollection.Count

<span style="color: #606060">  51:</span>         

<span style="color: #606060">  52:</span>         <span style="color: #008000">\'Get the collection items</span>

<span style="color: #606060">  53:</span>         items = objCollection.Items

<span style="color: #606060">  54:</span>         

<span style="color: #606060">  55:</span>         ix =  0

<span style="color: #606060">  56:</span>         <span style="color: #0000ff">Do</span> <span style="color: #0000ff">While</span> ix < count

<span style="color: #606060">  57:</span>             <span style="color: #008000">\'Check if the exit condition holds true</span>

<span style="color: #606060">  58:</span>             <span style="color: #0000ff">If</span> Eval(strExitCondition) <span style="color: #0000ff">Then</span>

<span style="color: #606060">  59:</span>                 dicResults(ix) = <span style="color: #006080">"Iteration "</span> & ix & <span style="color: #006080">" not performed."</span> & vbNewLine & <span style="color: #006080">"Condition \'"</span> & strExitCondition & <span style="color: #006080">"\' holds true. Exiting iterator."</span>

<span style="color: #606060">  60:</span>                 <span style="color: #0000ff">Exit</span> <span style="color: #0000ff">Do</span>

<span style="color: #606060">  61:</span>             <span style="color: #0000ff">End</span> <span style="color: #0000ff">If</span>

<span style="color: #606060">  62:</span>             

<span style="color: #606060">  63:</span>             <span style="color: #008000">\'This statement performs the process/operation on the current item</span>

<span style="color: #606060">  64:</span>             dicResults(ix) = ptrFunction(items(ix))

<span style="color: #606060">  65:</span>             

<span style="color: #606060">  66:</span>             <span style="color: #008000">\'Increment the counter</span>

<span style="color: #606060">  67:</span>             ix = ix + 1

<span style="color: #606060">  68:</span>         <span style="color: #0000ff">Loop</span>

<span style="color: #606060">  69:</span>         

<span style="color: #606060">  70:</span>         <span style="color: #008000">\'Return Dictionary with the results</span>

<span style="color: #606060">  71:</span>         <span style="color: #0000ff">Set</span> Run = dicResults

<span style="color: #606060">  72:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  73:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>

<span style="color: #606060">  74:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  75:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  76:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

<span style="color: #606060">  77:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 1. The Iterator Class

 

The reader certainly noticed the way an additional exit condition expression is passed to the Iterator (as a String) and how it is evaluated at the beginning of each iteration, before the actual operation is performed. This is also a powerful trick: for instance, we may pass to the Iterator Run method an exit condition that checks if an error occurred (“Err.Number <> 0″), after adding the On Error Resume Next and On Error Goto 0 statements at the appropriate places within the method.

Now, by implementing the Command Wrapper design pattern, let us define the arithmetic operations of addition, subtraction, multiplication, division, raising to a power and calculating the square root:

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Class</span> Add

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   5:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Default</span> <span style="color: #0000ff">Function</span> Exec(<span style="color: #0000ff">ByVal</span> arrInt)

<span style="color: #606060">   6:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   7:</span>     <span style="color: #008000">\'Function:    Exec</span>

<span style="color: #606060">   8:</span>     <span style="color: #008000">\'Executes the Command Wrapper\'s functionality</span>

<span style="color: #606060">   9:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  10:</span>     <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">  11:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  12:</span>     <span style="color: #008000">\'Arguments:</span>

<span style="color: #606060">  13:</span>     <span style="color: #008000">\'    ByVal arrInt - As Array</span>

<span style="color: #606060">  14:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  15:</span>     <span style="color: #008000">\'Returns:</span>

<span style="color: #606060">  16:</span>     <span style="color: #008000">\'    Result of operation</span>

<span style="color: #606060">  17:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  18:</span>     <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  19:</span>     <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks</span>

<span style="color: #606060">  20:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  21:</span>     <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  22:</span>     <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  23:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  24:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  25:</span>         Exec = arrInt(0)+arrInt(1)

<span style="color: #606060">  26:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  27:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>

<span style="color: #606060">  28:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  29:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  30:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

<span style="color: #606060">  31:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 2. Addition

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Class</span> Subtract

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   5:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Default</span> <span style="color: #0000ff">Function</span> Exec(<span style="color: #0000ff">ByVal</span> arrInt)

<span style="color: #606060">   6:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   7:</span>     <span style="color: #008000">\'Function:    Exec</span>

<span style="color: #606060">   8:</span>     <span style="color: #008000">\'Executes the Command Wrapper\'s functionality</span>

<span style="color: #606060">   9:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  10:</span>     <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">  11:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  12:</span>     <span style="color: #008000">\'Arguments:</span>

<span style="color: #606060">  13:</span>     <span style="color: #008000">\'    ByVal arrInt - As Array</span>

<span style="color: #606060">  14:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  15:</span>     <span style="color: #008000">\'Returns:</span>

<span style="color: #606060">  16:</span>     <span style="color: #008000">\'    Result of operation</span>

<span style="color: #606060">  17:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  18:</span>     <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  19:</span>     <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks</span>

<span style="color: #606060">  20:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  21:</span>     <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  22:</span>     <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  23:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  24:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  25:</span>         Exec = arrInt(0)-arrInt(1)

<span style="color: #606060">  26:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  27:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>

<span style="color: #606060">  28:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  29:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  30:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

<span style="color: #606060">  31:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 3. Subtraction

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Class</span> Multiply

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   5:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Default</span> <span style="color: #0000ff">Function</span> Exec(<span style="color: #0000ff">ByVal</span> arrInt)

<span style="color: #606060">   6:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   7:</span>     <span style="color: #008000">\'Function:    Exec</span>

<span style="color: #606060">   8:</span>     <span style="color: #008000">\'Executes the Command Wrapper\'s functionality</span>

<span style="color: #606060">   9:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  10:</span>     <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">  11:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  12:</span>     <span style="color: #008000">\'Arguments:</span>

<span style="color: #606060">  13:</span>     <span style="color: #008000">\'    ByVal arrInt - As Array</span>

<span style="color: #606060">  14:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  15:</span>     <span style="color: #008000">\'Returns:</span>

<span style="color: #606060">  16:</span>     <span style="color: #008000">\'    Result of operation</span>

<span style="color: #606060">  17:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  18:</span>     <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  19:</span>     <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks</span>

<span style="color: #606060">  20:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  21:</span>     <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  22:</span>     <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  23:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  24:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  25:</span>         Exec = arrInt(0)*arrInt(1)

<span style="color: #606060">  26:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  27:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>

<span style="color: #606060">  28:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  29:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  30:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

<span style="color: #606060">  31:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 4. Multiplication

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Class</span> Divide

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   5:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Default</span> <span style="color: #0000ff">Function</span> Exec(<span style="color: #0000ff">ByVal</span> arrInt)

<span style="color: #606060">   6:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   7:</span>     <span style="color: #008000">\'Function:    Exec</span>

<span style="color: #606060">   8:</span>     <span style="color: #008000">\'Executes the Command Wrapper\'s functionality</span>

<span style="color: #606060">   9:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  10:</span>     <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">  11:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  12:</span>     <span style="color: #008000">\'Arguments:</span>

<span style="color: #606060">  13:</span>     <span style="color: #008000">\'    ByVal arrInt - As Array</span>

<span style="color: #606060">  14:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  15:</span>     <span style="color: #008000">\'Returns:</span>

<span style="color: #606060">  16:</span>     <span style="color: #008000">\'    Result of operation</span>

<span style="color: #606060">  17:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  18:</span>     <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  19:</span>     <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks</span>

<span style="color: #606060">  20:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  21:</span>     <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  22:</span>     <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  23:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  24:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  25:</span>         <span style="color: #0000ff">If</span> arrInt(1) <> 0 <span style="color: #0000ff">Then</span>

<span style="color: #606060">  26:</span>             Exec = arrInt(0)/arrInt(1)

<span style="color: #606060">  27:</span>          <span style="color: #0000ff">Else</span>

<span style="color: #606060">  28:</span>              Exec = <span style="color: #006080">"Error: Division by zero."</span>

<span style="color: #606060">  29:</span>          <span style="color: #0000ff">End</span> <span style="color: #0000ff">If</span>

<span style="color: #606060">  30:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  31:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>

<span style="color: #606060">  32:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  33:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  34:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

<span style="color: #606060">  35:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 5. Division

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Class</span> Power

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   5:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Default</span> <span style="color: #0000ff">Function</span> Exec(<span style="color: #0000ff">ByVal</span> arrInt)

<span style="color: #606060">   6:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   7:</span>     <span style="color: #008000">\'Function:    Exec</span>

<span style="color: #606060">   8:</span>     <span style="color: #008000">\'Executes the Command Wrapper\'s functionality</span>

<span style="color: #606060">   9:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  10:</span>     <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">  11:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  12:</span>     <span style="color: #008000">\'Arguments:</span>

<span style="color: #606060">  13:</span>     <span style="color: #008000">\'    ByVal arrInt - As Array</span>

<span style="color: #606060">  14:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  15:</span>     <span style="color: #008000">\'Returns:</span>

<span style="color: #606060">  16:</span>     <span style="color: #008000">\'    Result of operation</span>

<span style="color: #606060">  17:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  18:</span>     <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  19:</span>     <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks</span>

<span style="color: #606060">  20:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  21:</span>     <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  22:</span>     <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  23:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  24:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  25:</span>         Exec = arrInt(0)^arrInt(1)

<span style="color: #606060">  26:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  27:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>

<span style="color: #606060">  28:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  29:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  30:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

<span style="color: #606060">  31:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 6. Raise to a Power

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Class</span> SquareRoot

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   5:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Default</span> <span style="color: #0000ff">Function</span> Exec(<span style="color: #0000ff">ByVal</span> varNumber)

<span style="color: #606060">   6:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   7:</span>     <span style="color: #008000">\'Function:    Exec</span>

<span style="color: #606060">   8:</span>     <span style="color: #008000">\'Executes the Command Wrapper\'s functionality</span>

<span style="color: #606060">   9:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  10:</span>     <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">  11:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  12:</span>     <span style="color: #008000">\'Arguments:</span>

<span style="color: #606060">  13:</span>     <span style="color: #008000">\'    ByVal varNumber - As Variant (Numeric))</span>

<span style="color: #606060">  14:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  15:</span>     <span style="color: #008000">\'Returns:</span>

<span style="color: #606060">  16:</span>     <span style="color: #008000">\'    Result of operation</span>

<span style="color: #606060">  17:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  18:</span>     <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  19:</span>     <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks</span>

<span style="color: #606060">  20:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  21:</span>     <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  22:</span>     <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  23:</span>     <span style="color: #008000">\'</span>

<span style="color: #606060">  24:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  25:</span>         <span style="color: #0000ff">If</span> varNumber <> 0 <span style="color: #0000ff">Then</span>

<span style="color: #606060">  26:</span>             Exec = Sqr(varNumber)

<span style="color: #606060">  27:</span>          <span style="color: #0000ff">Else</span>

<span style="color: #606060">  28:</span>              Exec = <span style="color: #006080">"Error: Square root for zero."</span>

<span style="color: #606060">  29:</span>          <span style="color: #0000ff">End</span> <span style="color: #0000ff">If</span>

<span style="color: #606060">  30:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  31:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>

<span style="color: #606060">  32:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  33:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  34:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

<span style="color: #606060">  35:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 7. Square Root

 

Now let us define the objects on which these operations will be performed. As we are in the realm of arithmetic operations, obviously the objects must be numbers.

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Class</span> Number

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span>     <span style="color: #0000ff">Private</span> m_intNumber

<span style="color: #606060">   5:</span>  

<span style="color: #606060">   6:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   7:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Default</span> <span style="color: #0000ff">Property</span> <span style="color: #0000ff">Get</span> Value()

<span style="color: #606060">   8:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   9:</span>         Value = m_intNumber

<span style="color: #606060">  10:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  11:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Property</span>

<span style="color: #606060">  12:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  13:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  14:</span>     <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Property</span> <span style="color: #0000ff">Let</span> Value(<span style="color: #0000ff">ByVal</span> intNumber)

<span style="color: #606060">  15:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  16:</span>         m_intNumber = intNumber

<span style="color: #606060">  17:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  18:</span>     <span style="color: #0000ff">End</span> <span style="color: #0000ff">Property</span>

<span style="color: #606060">  19:</span>     <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  20:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  21:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Class</span>

<span style="color: #606060">  22:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 8. The Number Class

 

We need a “constructor” function to create initialized instances of Number, as follows:

<span style="color: #606060">   1:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Public</span> <span style="color: #0000ff">Function</span> CreateNumber(<span style="color: #0000ff">ByVal</span> intNumber)

<span style="color: #606060">   3:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">   4:</span> <span style="color: #008000">\'Function:    CreateNumber</span>

<span style="color: #606060">   5:</span> <span style="color: #008000">\'Creates a new instance of class Number</span>

<span style="color: #606060">   6:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">   7:</span> <span style="color: #008000">\'Remarks:</span>

<span style="color: #606060">   8:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">   9:</span> <span style="color: #008000">\'Arguments:</span>

<span style="color: #606060">  10:</span> <span style="color: #008000">\'    ByVal intNumber - As Integer</span>

<span style="color: #606060">  11:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">  12:</span> <span style="color: #008000">\'Returns:</span>

<span style="color: #606060">  13:</span> <span style="color: #008000">\'    Initialized Number object</span>

<span style="color: #606060">  14:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">  15:</span> <span style="color: #008000">\'Owner:</span>

<span style="color: #606060">  16:</span> <span style="color: #008000">\'    Meir Bar-Tal, SOLMAR Knowledge Networks</span>

<span style="color: #606060">  17:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">  18:</span> <span style="color: #008000">\'Date:</span>

<span style="color: #606060">  19:</span> <span style="color: #008000">\'    17-Jan-2009</span>

<span style="color: #606060">  20:</span> <span style="color: #008000">\'</span>

<span style="color: #606060">  21:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  22:</span>     <span style="color: #0000ff">Set</span> CreateNumber = <span style="color: #0000ff">New</span> Number

<span style="color: #606060">  23:</span>     CreateNumber.Value = intNumber

<span style="color: #606060">  24:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

<span style="color: #606060">  25:</span> <span style="color: #0000ff">End</span> <span style="color: #0000ff">Function</span>

<span style="color: #606060">  26:</span> <span style="color: #008000">\'-------------------------------------------------------------------------------</span>

Snippet 9. The CreateNumber Function 

 

And to end this section, let us illustrate how the entities defined above work in practice. Naturally, the example will deal with performing certain types of calculations to a list of numbers.

<span style="color: #606060">   1:</span> <span style="color: #008000">\'Create a factor number to be used in the arithmetic operations</span>

<span style="color: #606060">   2:</span> <span style="color: #0000ff">Set</span> factor = CreateNumber(5)

<span style="color: #606060">   3:</span>  

<span style="color: #606060">   4:</span> <span style="color: #008000">\'Create a collection of number pairs using a dictionary</span>

<span style="color: #606060">   5:</span> <span style="color: #0000ff">Set</span> dic = CreateObject(<span style="color: #006080">"Scripting.Dictionary"</span>)

<span style="color: #606060">   6:</span> dic.Add <span style="color: #006080">"0"</span>, Array(CreateNumber(0), factor)

<span style="color: #606060">   7:</span> dic.Add <span style="color: #006080">"1"</span>, Array(CreateNumber(1), factor)

<span style="color: #606060">   8:</span> dic.Add <span style="color: #006080">"2"</span>, Array(CreateNumber(2), factor)

<span style="color: #606060">   9:</span> dic.Add <span style="color: #006080">"3"</span>, Array(CreateNumber(3), factor)

<span style="color: #606060">  10:</span> dic.Add <span style="color: #006080">"4"</span>, Array(CreateNumber(4), factor)

<span style="color: #606060">  11:</span> dic.Add <span style="color: #006080">"5"</span>, Array(CreateNumber(5), factor)

<span style="color: #606060">  12:</span> dic.Add <span style="color: #006080">"6"</span>, Array(CreateNumber(6), factor)

<span style="color: #606060">  13:</span> dic.Add <span style="color: #006080">"7"</span>, Array(CreateNumber(7), factor)

<span style="color: #606060">  14:</span> dic.Add <span style="color: #006080">"8"</span>, Array(CreateNumber(8), factor)

<span style="color: #606060">  15:</span> dic.Add <span style="color: #006080">"9"</span>, Array(CreateNumber(9), factor)

<span style="color: #606060">  16:</span>  

<span style="color: #606060">  17:</span> <span style="color: #008000">\'Create Iterator object</span>

<span style="color: #606060">  18:</span> <span style="color: #0000ff">Set</span> objIterator = <span style="color: #0000ff">New</span> Iterator

<span style="color: #606060">  19:</span>  

<span style="color: #606060">  20:</span> <span style="color: #008000">\'Set exit condition</span>

<span style="color: #606060">  21:</span> strExitCondition = <span style="color: #006080">"GlobalStatus = 2"</span>

<span style="color: #606060">  22:</span>  

<span style="color: #606060">  23:</span> <span style="color: #008000">\'Set GlobalStatus variable</span>

<span style="color: #606060">  24:</span> GlobalStatus = 0

<span style="color: #606060">  25:</span>  

<span style="color: #606060">  26:</span> <span style="color: #008000">\'Create Multiplication Operation</span>

<span style="color: #606060">  27:</span> <span style="color: #0000ff">Set</span> ptrFunction = <span style="color: #0000ff">New</span> Multiply

<span style="color: #606060">  28:</span> <span style="color: #008000">\'Print result</span>

<span style="color: #606060">  29:</span> Msgbox Join(objIterator(dic, ptrFunction, strExitCondition).Items, vbNewLine), vbOKOnly+vbInformation, <span style="color: #006080">"Iterator Example - Multiplication by "</span> & factor

<span style="color: #606060">  30:</span>  

<span style="color: #606060">  31:</span> <span style="color: #008000">\'Create Addition Operation</span>

<span style="color: #606060">  32:</span> <span style="color: #0000ff">Set</span> ptrFunction = <span style="color: #0000ff">New</span> Add

<span style="color: #606060">  33:</span> <span style="color: #008000">\'Print result</span>

<span style="color: #606060">  34:</span> Msgbox Join(objIterator(dic, ptrFunction, strExitCondition).Items, vbNewLine), vbOKOnly+vbInformation, <span style="color: #006080">"Iterator Example - Addition of "</span> & factor

<span style="color: #606060">  35:</span>  

<span style="color: #606060">  36:</span> <span style="color: #008000">\'Create Subtraction Operation</span>

<span style="color: #606060">  37:</span> <span style="color: #0000ff">Set</span> ptrFunction = <span style="color: #0000ff">New</span> Subtract

<span style="color: #606060">  38:</span> <span style="color: #008000">\'Print result</span>

<span style="color: #606060">  39:</span> Msgbox Join(objIterator(dic, ptrFunction, strExitCondition).Items, vbNewLine), vbOKOnly+vbInformation, <span style="color: #006080">"Iterator Example - Subtraction of "</span> & factor

<span style="color: #606060">  40:</span>  

<span style="color: #606060">  41:</span> <span style="color: #008000">\'Create Division Operation</span>

<span style="color: #606060">  42:</span> <span style="color: #0000ff">Set</span> ptrFunction = <span style="color: #0000ff">New</span> Divide

<span style="color: #606060">  43:</span> <span style="color: #008000">\'Print result</span>

<span style="color: #606060">  44:</span> Msgbox Join(objIterator(dic, ptrFunction, strExitCondition).Items, vbNewLine), vbOKOnly+vbInformation, <span style="color: #006080">"Iterator Example - Division by "</span> & factor

<span style="color: #606060">  45:</span>  

<span style="color: #606060">  46:</span> <span style="color: #008000">\'Create Power Operation</span>

<span style="color: #606060">  47:</span> <span style="color: #0000ff">Set</span> ptrFunction = <span style="color: #0000ff">New</span> Power

<span style="color: #606060">  48:</span> <span style="color: #008000">\'Print result</span>

<span style="color: #606060">  49:</span> Msgbox Join(objIterator(dic, ptrFunction, strExitCondition).Items, vbNewLine), vbOKOnly+vbInformation, <span style="color: #006080">"Iterator Example - Raise number to the Power of "</span> & factor

<span style="color: #606060">  50:</span>  

<span style="color: #606060">  51:</span> <span style="color: #008000">\'Create a collection of numbers using a dictionary</span>

<span style="color: #606060">  52:</span> <span style="color: #0000ff">Set</span> dic = CreateObject(<span style="color: #006080">"Scripting.Dictionary"</span>)

<span style="color: #606060">  53:</span> dic.Add <span style="color: #006080">"0"</span>, CreateNumber(100)

<span style="color: #606060">  54:</span> dic.Add <span style="color: #006080">"1"</span>, CreateNumber(200)

<span style="color: #606060">  55:</span> dic.Add <span style="color: #006080">"2"</span>, CreateNumber(300)

<span style="color: #606060">  56:</span> dic.Add <span style="color: #006080">"3"</span>, CreateNumber(400)

<span style="color: #606060">  57:</span> dic.Add <span style="color: #006080">"4"</span>, CreateNumber(400)

<span style="color: #606060">  58:</span> dic.Add <span style="color: #006080">"5"</span>, CreateNumber(500)

<span style="color: #606060">  59:</span> dic.Add <span style="color: #006080">"6"</span>, CreateNumber(600)

<span style="color: #606060">  60:</span> dic.Add <span style="color: #006080">"7"</span>, CreateNumber(700)

<span style="color: #606060">  61:</span> dic.Add <span style="color: #006080">"8"</span>, CreateNumber(800)

<span style="color: #606060">  62:</span> dic.Add <span style="color: #006080">"9"</span>, CreateNumber(900)

<span style="color: #606060">  63:</span>  

<span style="color: #606060">  64:</span> <span style="color: #008000">\'Create Square Root Operation</span>

<span style="color: #606060">  65:</span> <span style="color: #0000ff">Set</span> ptrFunction = <span style="color: #0000ff">New</span> SquareRoot

<span style="color: #606060">  66:</span>  

<span style="color: #606060">  67:</span> <span style="color: #008000">\'Print result</span>

<span style="color: #606060">  68:</span> Msgbox Join(objIterator(dic, ptrFunction, strExitCondition).Items, vbNewLine), vbOKOnly+vbInformation, <span style="color: #006080">"Iterator Example - Square Root"</span>

<span style="color: #606060">  69:</span>  

<span style="color: #606060">  70:</span> <span style="color: #008000">\'Dispose of Iterator</span>

<span style="color: #606060">  71:</span> <span style="color: #0000ff">Set</span> objIterator = <span style="color: #0000ff">Nothing</span>

Snippet 10. Using the Iterator

 

Note: The code is full of comments to make it easier for the reader to understand. In case of need, please comment below the article and I will reply and give assistance.

 

Summary

This article has shown how to build and use a generic iterator written in VB Script. I pinpointed the motivation for building such a mechanism and explained its advantages for improving our development practices. I wish to encourage the readers to put hands on and try to implement and use the techniques described here and in other articles at www.AdvancedQTP.com, and to share with the community their experience in these matters. As aforesaid, please don’t hesitate to contact us or post your questions here below.

, , , , , , ,

Implementing a GUI Layer with Classes

1
by on December 20, 2008 at 19:46

A Russian version of this article is available here

 

Abstract

This article describes a powerful technique that exploits Object Oriented Design Patterns, QTP descriptive programming (DP) and the Dictionary object to pack together GUI objects with their corresponding business oriented functions. The article includes a valuable bonus: a highly effective tip that enables to exit a test smoothly, preventing QTP from getting stuck when it fails to identify GUI objects during runtime.

 

Introduction

A central issue in automation development is how to reduce the effort spent on maintenance. Questions such as: “Should we use an Object Repository (OR) or Descriptive Programming (DP)? if an OR is chosen, then should we use a shared OR or a local OR for each Action? If DP is selected, then what is the optimal way to implement it?” are quite ubiquitous and the answers given can vary depending on the particular characteristics of the project, but many times also on the individual personalities involved.

In this article I will analyze the concept of Test Objects in the context of Object Oriented principles. I shall attempt to show that QTP’s Object Repository does not fit well with such principles and what is the impact of this on automation development viz-a-viz cost-effectiveness and maintainability. Subsequently, I will describe an expansion of the OR concept that is compatible with OO principles – the GUI Layer. This concept has been adopted by SOLMAR’s automation experts based on the observation that it is possible to maximize code reusability (and thus boosting maintainability) by undertaking an OO approach that breaks an automation project into several abstraction levels.

 

Test Objects and Run-Time Objects

A test object encapsulates a reference to a run-time object – the real world object that is instantiated in the application under test (AUT). A test object stores the description of the referenced object, which is a set of attributes and values that serve as criteria to locate the right object during the test run session. This description is pretty much like the one we keep in mind to identify our blind date partner at the meeting time (“I’m brunette, slim and tall, and will be wearing a red dress and shoes”, you’ve been told on the phone).

Similarly, QTP uses such a description to query the OS if there’s an object currently loaded to the machine’s RAM. If the answer is positive, the OS returns the address of the object (a reference or pointer) and hence QTP gains access to the object’s public methods and properties, since the private methods and properties are hidden from view by definition (so QTP never makes progress to the private domain, as you would with the brunette, if lucky!).

Though this description may seem overly simplified, basically it is this process that enables us to interact with the GUI (and other) objects using QTP or other automation tools (Test Partner, Test Complete, Rational Robot, AutoIt, etc.). A more detailed account of the distinction between Test Objects and Run-Time Objects is available in Yaron Assa’s article “Differences and Connections between Runtime Objects and Test Objects” (2009).

 

Test Objects and the Object Repository

The above account of test objects makes it seem only natural to think of them as data entities. What do I mean by this? Based on a description composed of a set of properties and values, QTP is able to identify a GUI object among all the objects currently loaded to the machine’s RAM. This is similar to the retrieval of a particular record from a database table based on a SQL WHERE clause. The difference is that in this case, it is the OS (Windows), not the DB, the one that returns the record. Moreover, this record contains a single field that stores a reference to the actual runtime object. Following this line of thought, it seems only natural to have these entities – Test Objects – stored in a database, which essentially is exactly what the Object Repository is (a side note, if you noticed the bdb suffixed files in QTP tests, then note that bdb actually stands for Berkeley Data Base – an Oracle product).

What, then, seems to be the problem with the OR approach? Tests Objects are really data entities, but at the end of the day we need to perform actions on Runtime objects, insomuch that the above account leaves us with an incomplete picture. I will explain below why.

It is true that the OR, if used properly, reflects a best practice according to which we ought to store the description of each GUI (Test) Object only once to reduce maintenance costs. However, in order to achieve this a great effort must be invested also into managing the automation project properly, because ad-hoc requests and resource limitations can endanger the integrity of such a precious resource. For instance, suppose that two automation developers work simultaneously with a shared repository, but one is maintaining the scripts for the last version and the other is developing new scripts for the newest one. Given that GUI changes are made quite regularly from one AUT version to the next, it will not be too long until the OR becomes cluttered with new, redundant, test objects required by the developer working on the newest version. And this is a relatively good result. The worst case would be one where each developer would change in turn the descriptions of existing objects to match their requirements, damaging irrevocably the ROI on the automation project as a whole. Of course, there are solutions to such situations, such as keeping separate versions of the OR that match each AUT version and using configuration management software in the automation project. But, as aforesaid, this requires good project management, which is not always available.

Moreover, keeping versions of the OR leaves one issue still open. Because automated tests are, in general, regressive by nature, then maintenance would certainly pertain not only to the OR but also that the code that implements the actual GUI manipulations and verifications, as well as the input data and expected results data. If the common situation would have been that only the object descriptions changed from one AUT version to the next, then using an OR would be an excellent solution. However, more often than not, GUI changes reflect modifications in the way the AUT actually functions, which are many times also accompanied by even deeper changes – in the database structure, for example. So, changes in the AUT would require matching changes in both the OR and the scripts that refer to its test objects, as well as in the data sources that feed these scripts. Taking into account the fact that scripts are most of the time maintained, not developed, the aforementioned line of analysis lead me to the conclusion that something in the OR concept is faulty regarding the manageability of a large-scale automation project.

 

The Object Oriented View

The Object Oriented software paradigm is based on the basic concepts of encapsulation, inheritance and polymorphism. Encapsulation basically means to pack together functionality and data structures that belong together, and the package through which we achieve this is called a class. In fact, classes mostly are representations of data entities (like Customer, Product, etc.). The difference between a fully featured class and a mere data structure (as in the C programming language) is that the class also wraps the functions available for use with the class data fields. These are commonly referred to as the class methods. For example, a typical class for a Customer would pack the data fields that define the customer as a data entity (customerId, firstName, lastName, phoneNo, etc.) together with functions such as setCustomerId, getCustomerId, which assign and retrieve the value of the respective field, as well as others like getCustomerAge and getCustomerBalance which would perform a calculation based on the class fields current values and then return the result.

Inheritance is deeply related to one of the major goals of the OO approach – reusability. In OO programming languages like C++ and Java, reusability comes into play by means of the ability to device a new class that “inherits” the fields and methods of a previously defined class (also called a base class) and expands on these according to the specific requirements that raised the need for the new class. Polymorphism is yet another powerful concept that enables one to design more than one version of a function (with the same name but different number or types of arguments) to be able to handle transparently different situations within the application context. For instance, we might need to handle numbers of different types (float, int) and so instead of having a single function with conditional statements that check for the value type that was passed, we’ll have several functions with the same name but different signatures (different type of arguments). Hence in our code that uses these functions we would not need to use casting; we would use the same interface in both cases, with the correct process ultimately invoked by the runtime environment. However in QTP these two last concepts of inheritance and polymorphism cannot be implemented based on the VB Script language which provides very rudimentary support for working with classes. Nevertheless, we shall see later that this is not a reason to refrain from using classes in testing automation.

The public and private notions mentioned above are of great importance with regard to encapsulation and inheritance. They enable the code developer to determine which fields (also called properties or attributes of the class) and methods will be actually accessible to the world outside the class (public), and which will remain for internal (private) use of the class members (i.e., the class fields and methods). The OO methodology is not new and has been widely put into practice in the software design and development industry. It is recognized as one approach that makes the resulting code more reusable, maintainable, readable, scalable, extensible and, yes, also more testable (as huge pieces of functionality are broken down into smaller, self-contained capsules or packages). Of course, making good use of the methodology requires deep analytical skills that enable the code designer to infer correctly from the requirements of the software under construction which are the appropriate entities involved and how they are interrelated.

How this relates to the way we approach, or should approach, the testing automation challenge? Let us delve into it below.

 

Testing Automation and Software Development

One thing I have witnessed throughout my career is that QA professionals of all levels treat testing automation as just another testing activity. Not surprisingly, it is not rare to find automation professionals that think likewise since many times the role of automation. Sometimes this is even reflected by the job title given to the automation staff: “automatic tester” is one of the absurd titles I have encountered several years ago. This reflects a misunderstanding of the role of the Automation Developer and, as I shall explain in what follows, this belief is a true misconception of the essence of testing automation.

The task of automating tests should not be treated differently than any other content specific automation task. Generally speaking, computer programs that perform operations instead of humans implement automation. So basically any block of code is a kind of automation device, or robot. Scripts that carry out operations on GUI objects – such as QTP tests – are not different than any other piece of software. Put it in other words, a testing automation project is, indeed, a specific kind of software development project. As with any software product, an automation project also has its own SRS (Software Requirements Specifications) document: the STD (Software Test Design) “document” (or tests design in a quality management tool such as HP’s Quality Center or Orcanos’ QPack), which should guide the automation developer in the implementation of the required code.

If so, then an automation project should be treated and managed as any other development project and, in fact, even more so. This is because the automation developer faces challenges that are usually not felt by the developers of the AUT. Some of the main challenges are the following:

  1. First, the automation developer must have an overall view of the AUT, as the automation scripts may cover a large part of the AUT’s main functionality. Members of the AUT’s development team do not necessarily need this, as the division of labor among the different teams is typically coordinated by the team leaders and a project manager. So a developer can focus on the part assigned to him and do it well even without having in depth knowledge of the whole system.
  2. Second, the automation scripts should be mapped to the test design, as they should emulate the steps done by a human tester. But, quite often, automation developers find out that the test design leaves many open issues that need to be resolved beforehand, as the automation scripts do not possess the flexibility and ingenuity of a human tester during runtime.
  3. Third, the GUI controls used by the development team, and also the AUT’s behavior may pose a real technological challenge regarding the identification of the objects by QTP (or other tools). Many times solutions that extend the basic capacities of the tools are required, as is the case with third-party and custom controls.

 

Following the above exposition, I think we may conclude that an automation project should be definitely managed as a development project. If this is so, why should one fall again and again victim to the fallacy of treating an automation project as a trivial task (does “record-replay” remind you of something?), where the opposite is true? Why not, then, approach to do it using the most widely accepted development method – Object Oriented – to attain the best possible results? In what follows I will describe a method to implement automation “scripts” based on an extension to the OR concept – the GUI Layer, which is based on solid OO principles.

 

The Concept of Layers

I hope to have managed so far to make clear why the OR approach, with scripts spread all around the place, is far from being the optimal approach. Now, following the above discussion, let us take a look at the concept of developing code in layers, especially the GUI Layer concept. In general, layers are useful to maximize reusability (recall the previous discussion on OO principles). I shall define a GUI Layer as a set of classes that pack (encapsulate) together the interfaces required to manipulate the AUT’s GUI objects in each application context. In other words, it is a set of classes that bridge between the AUT’s GUI (i.e., the test objects proper) and the Business or Application Layer, of which we shall have more to say later. In a sense, may be it would be more appropriate to call it the GUI-Business Bridge Layer, but it is already accustomed among the experts to call it a GUI Layer, to keep it short. I will illustrate below how such a layer can be built, and what are the benefits gained from adopting such an approach to testing automation.

Implementing the GUI Layer

Encapsulating Test Objects in Classes

Let us take a testing automation project on a typical AUT and see how the solution should be designed according to the approach outlined above. The first step would be to make a simple list of all application GUI contexts – i.e., the windows (pages in a Web application), dialogs, and popup messages. For each of these entities, which are containers of other GUI objects, we define a class, for example:

<span style="color: #0000ff;">Class</span> Login

<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>

<span style="color: #0000ff;">Class</span> MainWindow

<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>

<span style="color: #0000ff;">Class</span> CreateCustomer

<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>

and so on for each of the application contexts. Because QTP does not allow for direct instantiation of classes defined in external vbs files with the operator New, it is also required to define the following function that will return an instance of a GUI layer class (a kind of constructor function), as follows:

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> CreateLogin()
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Function: CreateLogin</span>
<span style="color: #008000;">\'Creates an instance of the Login class</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Arguments:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Returns:</span>
<span style="color: #008000;">\' Object - As Login</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' John Doe</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' dd-MMM-yyyy</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #0000ff;">Dim</span> objLogin

    <span style="color: #0000ff;">Set</span> objLogin = <span style="color: #0000ff;">New</span> Login

    <span style="color: #0000ff;">Set</span> CreateLogin = objLogin
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

The second step is, obviously, to define the members of each class. Now, since each class is, as aforesaid, a container of other GUI objects, we shall make use of the Scripting.Dictionary to store the references to the test objects contained in the window, dialog or page. (the  Dictionary object has been already extensively discussed in other articles published at AdvancedQTP‘s knowledge base). So, the first member I shall introduce here will be common to all GUI Layer classes, and I will define it as m_htChildObjects, for example:

<span style="color: #0000ff;">Class</span> Login
    <span style="color: #0000ff;">Private</span> m_htChildObjects <span style="color: #008000;">\'As Scripting.Dictionary</span>

<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>

<span style="color: #0000ff;">Class</span> MainWindow
    <span style="color: #0000ff;">Private</span> m_htChildObjects <span style="color: #008000;">\'As Scripting.Dictionary</span>

<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>

and so on for each of the application contexts (ht stands for HashTable, which is what the dictionary really is). The private member m_htChildObjects will be accessed through the class property ChildObjects. This property is defined as follows:

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Property: ChildObjects</span>
<span style="color: #008000;">\'Get and Set the m_htChildObjects member field</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\' R/W</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Arguments:</span>
<span style="color: #008000;">\' dic</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Returns:</span>
<span style="color: #008000;">\' m_htChildObjects As HashTable</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' John Doe</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' dd-MMM-yyyy</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> ChildObjects()
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #0000ff;">Set</span> ChildObjects = m_htChildObjects
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> ChildObjects(<span style="color: #0000ff;">ByRef</span> dic)
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #0000ff;">Set</span> m_htChildObjects = dic
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

The third step is to define the objects contained within each context. For this purpose, I will define a public method within the class called Init, as follows:

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> Init()
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Function: Init</span>
<span style="color: #008000;">\'Initializes the context and child objects</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Dependencies:</span>
<span style="color: #008000;">\' IsContextLoaded(htContext)</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Arguments:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Returns:</span>
<span style="color: #008000;">\' True/False</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' John Doe</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' dd-MMM-yyyy</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    ChildObjects = CreateObject(<span style="color: #006080;">"Scripting.Dictionary"</span>)

    <span style="color: #0000ff;">With</span> ChildObjects
        .Add <span style="color: #006080;">"Browser"</span>, Browser(<span style="color: #006080;">"name:=My App"</span>)
        .Add <span style="color: #006080;">"Page"</span>, ChildObjects(<span style="color: #006080;">"Browser"</span>).Page(<span style="color: #006080;">"title:=My App \- Login"</span>)
        .Add <span style="color: #006080;">"Username"</span>, ChildObjects(<span style="color: #006080;">"Page"</span>).WebEdit(<span style="color: #006080;">"html id:=Username"</span>)
        .Add <span style="color: #006080;">"Password"</span>, ChildObjects(<span style="color: #006080;">"Page"</span>).WebEdit(<span style="color: #006080;">"html id:=Password"</span>)
        .Add <span style="color: #006080;">"Submit"</span>, ChildObjects(<span style="color: #006080;">"Page"</span>).WebButton(<span style="color: #006080;">"outertext:=Submit"</span>)
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>

    <span style="color: #008000;">\'IsContextLoaded is a function that iterates through the Dictionary and checks if the GUI objects "exist"</span>
    Init = IsContextLoaded(ChildObjects)
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

 

The code snippet above shows a typical Init method for a GUI Layer class for a Web application Login page. The test objects are added as entries to the ChildObjects Dictionary, and their identity is defined using Descriptive Programming (DP). The reader can easily infer the analogy to the OR. It is thanks to this encapsulation method that we ensure that GUI objects are always defined at a single place. At the end of the function body you will notice that it returns the result of a call to the function IsContextLoaded which accepts as argument the Dictionary that stores the ChildObjects.

IsContextLoaded is defined in a separate common library, as follows:

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> IsContextLoaded(<span style="color: #0000ff;">ByRef</span> htContext)
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Function: IsContextLoaded</span>
<span style="color: #008000;">\'Checks that the current GUI context is loaded</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Iterates through the htContext (HashTable) items and executes the Exist method with 0 (zero) as parameter.</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Arguments:</span>
<span style="color: #008000;">\' ByRef htContext - As HashTable</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Returns:</span>
<span style="color: #008000;">\' True/False</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' Meir Bar-Tal, SOLMAR Knowledge Networks Ltd.</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' 11-Nov-2008</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'See Also:</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #0000ff;">Dim</span> ix, items, keys, strDetails, strAdditionalRemarks

    <span style="color: #008000;">\'---------------------------------------------------------------------------</span>
    items = htContext.Items
    keys = htContext.Keys

    <span style="color: #0000ff;">For</span> ix = 0 <span style="color: #0000ff;">To</span> htContext.Count-1
        IsContextLoaded = IsContextLoaded <span style="color: #0000ff;">And</span> items(ix).Exist(0)
        strDetails = strDetails & vbNewLine & <span style="color: #006080;">"Object #"</span> & ix+1 & <span style="color: #006080;">": \'"</span> & keys(ix) & <span style="color: #006080;">"\' was"</span>
        <span style="color: #0000ff;">If</span> IsContextLoaded <span style="color: #0000ff;">Then</span>
            intStatus = micPass
            strDetails = strDetails & <span style="color: #006080;">""</span>
            strAdditionalRemarks = <span style="color: #006080;">""</span>
        <span style="color: #0000ff;">Else</span>
            intStatus = micWarning
            strDetails = strDetails & <span style="color: #006080;">" not"</span>
            strAdditionalRemarks = <span style="color: #006080;">" Please check the object properties."</span>
        <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
        strDetails = strDetails & <span style="color: #006080;">" found."</span> & strAdditionalRemarks
    <span style="color: #0000ff;">Next</span>
    <span style="color: #008000;">\'---------------------------------------------------------------------------</span>

    Reporter.ReportEvent intStatus, <span style="color: #006080;">"IsContextLoaded"</span>, strDetails
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

And it returns True if all objects defined in the Dictionary are identified, or False if at least one object is not found. This function is generic and it is used in the projects I manage to ensure that QTP does not get stuck while attempting to perform some operation on a non-existing GUI object. Another benefit of this method is that it points exactly to the object we need to recheck and update, making maintenance much easier.

Encapsulating Business Methods in Classes

The next step after defining the child objects of the GUI context is to define the operations required to perform the application or business scenarios within the given context. This is easily done by implementing class methods. For example, the login class outlined above would need the following methods to begin with: SetUsername, SetPassword and Submit. These are shown below:

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> SetUsername()
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Function: SetUsername</span>
<span style="color: #008000;">\'Set the Username field</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Dependencies:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Arguments:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Returns:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' John Doe</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' dd-MMM-yyyy</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    ChildObjects(<span style="color: #006080;">"Username"</span>).<span style="color: #0000ff;">Set</span> GlobalDictionary(<span style="color: #006080;">"Username"</span>)
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> SetPassword()
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Function: SetPassword</span>
<span style="color: #008000;">\'Set the Password field</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Dependencies:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Arguments:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Returns:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' John Doe</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' dd-MMM-yyyy</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    ChildObjects(<span style="color: #006080;">"Password"</span>).<span style="color: #0000ff;">Set</span> GlobalDictionary(<span style="color: #006080;">"Password"</span>)
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #008000;">\'------------------------------------------------------------------------------- </span>

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> Submit()
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Function: Submit</span>
<span style="color: #008000;">\'Presses the Submit button</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Dependencies:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Arguments:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Returns:</span>
<span style="color: #008000;">\' N/A</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' John Doe</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' dd-MMM-yyyy</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    ChildObjects(<span style="color: #006080;">"Submit"</span>).Click

    <span style="color: #008000;">\'TODO: Verify data submission performed successfully</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

Notice the use of the GlobalDictionary to retrieve the values required by the functions, and the use of the ChildObjects property to retrieve through the test object a reference to the runtime object.

The next step would be to move on to the Business Layer, which implements an application scenario building on the strong foundations of the GUI Layer. For instance to perform the Login function based on the above example, we could wrap it with the following function:

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> do_login()
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Function: do_login</span>
<span style="color: #008000;">\'Implements the business logic of the do_login Action.</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Arguments:</span>
<span style="color: #008000;">\' None</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Returns:</span>
<span style="color: #008000;">\' Status</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' John Doe</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' dd-MMM-yyyy</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #0000ff;">Dim</span> intStatus, objLogin

    <span style="color: #0000ff;">Set</span> objLogin = CreateLogin()
    <span style="color: #0000ff;">If</span> objLogin.Init() <span style="color: #0000ff;">Then</span>
        objLogin.SetUsername()
        objLogin.SetPassword()
        objLogin.Submit()

        <span style="color: #008000;">\'If login succeeds</span>
        intStatus = micPass
    <span style="color: #0000ff;">Else</span>
        intStatus = micFail
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>

    do_login = intStatus
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

 

Notice the use the Login class outlined above and of its Init function as a precaution to ensure the GUI context is loaded, and not get stuck, as mentioned above. As you can also see, the code within the function above is easy to understand, and is not cluttered with the references to both the OR test objects and data sources as quite often is the case. If changes are made to the GUI objects of a given context, then the changes will be concentrated within a single package, both with respect to the object properties and to the functionality required to manipulate the context’s child objects. Yet another gain from this method is standardization. By implementing code this way we achieve a high degree of homogeneity in the code written by different developers, and thus enhancing the manageability of the automation project.

An advanced alternative to the last example is to pack such a Business Layer function using the Command Wrapper Design Pattern, as outlined in my article Function Pointers in VB Script (revised). For example:

<span style="color: #008000;">\'VB Script Document</span>
<span style="color: #0000ff;">Option</span> Explicit

<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">Class</span> do_login
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'Class: do_login</span>
<span style="color: #008000;">\'Encapsulates the do_login Action.</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Remarks:</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Owner:</span>
<span style="color: #008000;">\' John Doe</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'Date:</span>
<span style="color: #008000;">\' dd-MMM-yyyy</span>
<span style="color: #008000;">\'</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #008000;">\'Methods</span>
    <span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Run()
    <span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #008000;">\'Function: Run</span>
    <span style="color: #008000;">\'Implements the business logic of the do_login Action.</span>
    <span style="color: #008000;">\'</span>
    <span style="color: #008000;">\'Remarks:</span>
    <span style="color: #008000;">\'</span>
    <span style="color: #008000;">\'Arguments:</span>
    <span style="color: #008000;">\' None</span>
    <span style="color: #008000;">\'</span>
    <span style="color: #008000;">\'Returns:</span>
    <span style="color: #008000;">\' Status</span>
    <span style="color: #008000;">\'</span>
    <span style="color: #008000;">\'Owner:</span>
    <span style="color: #008000;">\' John Doe</span>
    <span style="color: #008000;">\'</span>
    <span style="color: #008000;">\'Date:</span>
    <span style="color: #008000;">\' dd-MMM-yyyy</span>
    <span style="color: #008000;">\'</span>
    <span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
        <span style="color: #0000ff;">Dim</span> intStatus

        <span style="color: #0000ff;">Set</span> objLogin = CreateLogin()
        <span style="color: #0000ff;">If</span> objLogin.Init() <span style="color: #0000ff;">Then</span>
            objLogin.SetUsername()
            objLogin.SetPassword()
            objLogin.Submit()

            <span style="color: #008000;">\'If login succeeds</span>
            intStatus = micPass
        <span style="color: #0000ff;">Else</span>
            intStatus = micFail
        <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>

        Run = intStatus
    <span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
    <span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #008000;">\'-------------------------------------------------------------------------------</span>

Adopting this method would enable the implementation of an advanced generic controller that loads its flow from an external data source such as an XML file. Such a controller device was developed by me together with my partners at SOLMAR Knowledge Networks as part of our generic Object Oriented comprehensive automation framework – My System.

Summary

This article has reviewed an alternative approach to code implementation in a testing automation project that is based on object-oriented principles. I have shown that automation scripting should not be treated differently than software development. Moreover, I tried to convey that logically following this line of thought leads to the conclusion that an automation project must be approached as a software development project par excellence, and suggested an expansion to the OR concept – which was shown as inadequate viz-a-viz the OO paradigm – to encapsulate the interfaces to the GUI objects: the GUI Layer. The article has provided a practical example of how to implement such a layer and how to invoke it using a Business Layer, and explained in detail the benefits of adopting this approach to obtain the maximum ROI from an automation project regarding maintainability, readability, scalability, extensibility and testability. Future articles will expand on this theme and guide the readers into how to gain from the implementation of other design patterns within the framework laid out in this article.

, , , , , ,

Function Pointers in VB Script (revised)

0
by on December 9, 2008 at 11:00

Abstract

Function pointers are used in C and C++ to enable callbacks and as a result, more generic and parsimonious coding. This article shows how to implement function pointers in VB Script using the Scripting.Dictionary object and the Command Wrapper design pattern. It is also shown how to implement “constructor” functions, callbacks land event handlers.

 

Keywords: function pointer, callback, delegate, scripting dictionary, hash table, design pattern, command wrapper, event handler.

Introduction

In the following sections I shall explain several basic concepts that are essential to understand the rationale behind the suggested method. The list might be non-exhaustive and the reader is hereby encouraged to recur to complementary sources to fill-in any eventual knowledge gaps.

Function Pointers

A function pointer is a variable that stores the memory address of a block of code that is programmed to fulfill a specific function. Function pointers are useful to avoid complex switch case structures; instead, they support direct access in run-time to previously loaded functions or class methods. This enables the construction of callback functions. A callback is, in essence, executable code that is passed as argument to a function. This enables more generic coding, by having lower-level modules calling higher-level functions or subroutines.

Function pointers are supported by programming languages like C and C++. A recent good introductory text on the subject with implementation examples is Lars Haendel (2005).

This article shows how to implement function pointers in VB script.

Design Patterns

Design patterns are well established generic design techniques whose aim is to make software design and the derived code more parsimonious, scalable, and reusable, as well as more easily maintainable. This article will delve only into the command wrapper design pattern, which is important for its purpose. For a more extensive explanation of design patterns, the reader is encouraged to read the literature in the field (e.g., Gamma, Helm, Johnson, & Vlissides, 1997).

The command-wrapper design pattern enables to load blocks of code dynamically to memory as well as other uses as will be reviewed below. The singleton design pattern is used to restrict instantiation of a class to one object. This is useful when there is no need for more than one object within a system (e.g., a reporter/logger object). For example, the factory design pattern usually implements the singleton design pattern to have a single interface from which to retrieve references to a specific class object or to a variety of class objects.

The Command Wrapper Design Pattern

The command wrapper design pattern is a technique that implements a function as a class, usually with a single public method. During run-time, it is possible to instantiate the class and thus to get a reference (or pointer) to the object. This way, the code embodied in such methods can be dynamically loaded, a feature that can be of great value in systems that are poor in resources. This is especially true when the to-be called function is rarely used. After creating the object, it is possible to execute its method according to need. For example, let us assume a function that adds numbers (note: this example is, of course, trivial. It is done just to illustrate the point.)

<span style="color: #606060;"> 1:</span> <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> Sum(<span style="color: #0000ff;">ByVal</span> arrNumbers)
<span style="color: #606060;"> 2:</span>     <span style="color: #0000ff;">Dim</span> ix
<span style="color: #606060;"> 3:</span>
<span style="color: #606060;"> 4:</span>     <span style="color: #008000;">\'Check if an array was passed. If so, there\'s nothing to do – exit function</span>
<span style="color: #606060;"> 5:</span>     <span style="color: #0000ff;">If</span> (<span style="color: #0000ff;">Not</span> IsArray(arrNumbers)) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 6:</span>         <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 7:</span>         <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 8:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 9:</span>
<span style="color: #606060;"> 10:</span>     Sum = 0
<span style="color: #606060;"> 11:</span>     <span style="color: #0000ff;">For</span> ix = LBound(arrNumbers) <span style="color: #0000ff;">To</span> UBound(arrNumbers)
<span style="color: #606060;"> 12:</span>         <span style="color: #0000ff;">If</span> (IsNumeric(arrNumbers(ix))) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 13:</span>             Sum = Sum + arrNumbers(ix)
<span style="color: #606060;"> 14:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 15:</span>             <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 16:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 17:</span>     <span style="color: #0000ff;">Next</span>
<span style="color: #606060;"> 18:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 19:</span>
<span style="color: #606060;"> 20:</span> <span style="color: #008000;">\'Test the function and display result returned by the Sum function</span>
<span style="color: #606060;"> 21:</span> MsgBox Sum(Array(23, 56, 78, 95, 114)), vbOKOnly, <span style="color: #006080;">"Result from simple function"</span>
<span style="color: #606060;"> 22:</span>
<span style="color: #606060;"> 23:</span>
<span style="color: #606060;"> 24:</span> <span style="color: #008000;">\'Now, let us convert this function into an object using the Command Wrapper design pattern:</span>
<span style="color: #606060;"> 25:</span> <span style="color: #0000ff;">Class</span> Sum
<span style="color: #606060;"> 26:</span>     <span style="color: #0000ff;">Private</span> m_arrVarNumbers
<span style="color: #606060;"> 27:</span>     <span style="color: #0000ff;">Private</span> m_varResult
<span style="color: #606060;"> 28:</span>
<span style="color: #606060;"> 29:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize()
<span style="color: #606060;"> 30:</span>         <span style="color: #008000;">\'Initialize the Numbers member as an empty array</span>
<span style="color: #606060;"> 31:</span>         <span style="color: #0000ff;">ReDim</span> m_arrVarNumbers(-1)
<span style="color: #606060;"> 32:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>
<span style="color: #606060;"> 33:</span>
<span style="color: #606060;"> 34:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> Init(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 35:</span>         Numbers = arrVarNumbers
<span style="color: #606060;"> 36:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 37:</span>
<span style="color: #606060;"> 38:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec()
<span style="color: #606060;"> 39:</span>         <span style="color: #0000ff;">Dim</span> ix, arrNumbers
<span style="color: #606060;"> 40:</span>
<span style="color: #606060;"> 41:</span>         <span style="color: #008000;">\'Check if an array was passed. If so, there\'s nothing to do – exit function</span>
<span style="color: #606060;"> 42:</span>         <span style="color: #0000ff;">If</span> (<span style="color: #0000ff;">Not</span> IsArray(Numbers)) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 43:</span>             <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 44:</span>             Exec = <span style="color: #006080;">"Invalid data type was supplied to perform the operation."</span>
<span style="color: #606060;"> 45:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 46:</span>         <span style="color: #0000ff;">ElseIf</span> (UBound(Numbers) - LBound(Numbers) + 1 <= 1) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 47:</span>                 <span style="color: #008000;">\'Array is empty or has single item - Add your error handling code here</span>
<span style="color: #606060;"> 48:</span>                 Exec = <span style="color: #006080;">"Not enough data was supplied to perform the operation."</span>
<span style="color: #606060;"> 49:</span>                 <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 50:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 51:</span>             arrNumbers = Numbers
<span style="color: #606060;"> 52:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 53:</span>
<span style="color: #606060;"> 54:</span>         Result = 0
<span style="color: #606060;"> 55:</span>         <span style="color: #0000ff;">For</span> ix = LBound(arrNumbers) <span style="color: #0000ff;">To</span> UBound(arrNumbers)
<span style="color: #606060;"> 56:</span>             <span style="color: #0000ff;">If</span> (IsNumeric(arrNumbers(ix))) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 57:</span>                 Result = Result + arrNumbers(ix)
<span style="color: #606060;"> 58:</span>             <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 59:</span>                 <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 60:</span>             <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 61:</span>         <span style="color: #0000ff;">Next</span>
<span style="color: #606060;"> 62:</span>         Exec = Result
<span style="color: #606060;"> 63:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 64:</span>
<span style="color: #606060;"> 65:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Numbers()
<span style="color: #606060;"> 66:</span>         Numbers = m_arrVarNumbers
<span style="color: #606060;"> 67:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 68:</span>
<span style="color: #606060;"> 69:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Numbers(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 70:</span>         m_arrVarNumbers = arrVarNumbers
<span style="color: #606060;"> 71:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 72:</span>
<span style="color: #606060;"> 73:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Result()
<span style="color: #606060;"> 74:</span>         Result = m_varResult
<span style="color: #606060;"> 75:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 76:</span>
<span style="color: #606060;"> 77:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Result(<span style="color: #0000ff;">ByVal</span> varResult)
<span style="color: #606060;"> 78:</span>         m_varResult = varResult
<span style="color: #606060;"> 79:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 80:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 81:</span>
<span style="color: #606060;"> 82:</span> <span style="color: #008000;">\'This function behaves as a constructor and returns an initialized instance of the class</span>
<span style="color: #606060;"> 83:</span> <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetSum(<span style="color: #0000ff;">ByVal</span> arrNumbers)
<span style="color: #606060;"> 84:</span>     <span style="color: #0000ff;">Set</span> GetSum = <span style="color: #0000ff;">New</span> Sum
<span style="color: #606060;"> 85:</span>     GetSum.Init(arrNumbers)
<span style="color: #606060;"> 86:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 87:</span>
<span style="color: #606060;"> 88:</span> <span style="color: #008000;">\'Test the class</span>
<span style="color: #606060;"> 89:</span> <span style="color: #0000ff;">Dim</span> objSum, arrNumbers
<span style="color: #606060;"> 90:</span>
<span style="color: #606060;"> 91:</span> <span style="color: #008000;">\'Assign an array of numbers</span>
<span style="color: #606060;"> 92:</span> arrNumbers = Array(23, 56, 78, 95, 114)
<span style="color: #606060;"> 93:</span>
<span style="color: #606060;"> 94:</span> <span style="color: #008000;">\'Get an instance of class Sum and initialize it with numbers array</span>
<span style="color: #606060;"> 95:</span> <span style="color: #0000ff;">Set</span> objSum = GetSum(arrNumbers)
<span style="color: #606060;"> 96:</span>
<span style="color: #606060;"> 97:</span> <span style="color: #008000;">\'Execute Sum (Exec method)</span>
<span style="color: #606060;"> 98:</span> objSum.Exec()
<span style="color: #606060;"> 99:</span>
<span style="color: #606060;"> 100:</span> <span style="color: #008000;">\'Display result stored in Result property</span>
<span style="color: #606060;"> 101:</span> MsgBox objSum.Result, vbOKOnly, <span style="color: #006080;">"Result from Result Property"</span>
<span style="color: #606060;"> 102:</span>
<span style="color: #606060;"> 103:</span> <span style="color: #008000;">\'Alternatively, display the result returned by the Exec method:</span>
<span style="color: #606060;"> 104:</span> MsgBox GetSum(Array(23, 56, 78, 95, 114)).Exec, vbOKOnly, <span style="color: #006080;">"Result from Exec method"</span>

 

The Scripting Dictionary

In VB Script, the scripting dictionary is an object that stores key-item pairs using a hashing algorithm. The items can be accessed using their corresponding keys. Basically, the dictionary is useful to store variant type, non-structured data, such as the catalog number of an item in an inventory list, or the authors of a list of books, and to retrieve the data using the unique keys supplied, as in the following example:

<span style="color: #606060;"> 1:</span> <span style="color: #0000ff;">Dim</span> dicBooksAuthors
<span style="color: #606060;"> 2:</span>
<span style="color: #606060;"> 3:</span> <span style="color: #008000;">\'Create an instance of the scripting dictionary class</span>
<span style="color: #606060;"> 4:</span> <span style="color: #0000ff;">Set</span> dicBookAuthors = CreateObject(<span style="color: #006080;">"Scripting.Dictionary"</span>)
<span style="color: #606060;"> 5:</span>
<span style="color: #606060;"> 6:</span> <span style="color: #0000ff;">With</span> dicBookAuthors
<span style="color: #606060;"> 7:</span>     <span style="color: #008000;">\'Add some books (keys) and authors (items)</span>
<span style="color: #606060;"> 8:</span>     .Add <span style="color: #006080;">"The Selfish Gene"</span>, <span style="color: #006080;">"Richard Dawkins"</span>
<span style="color: #606060;"> 9:</span>     .Add <span style="color: #006080;">"The Mismeasure of Man"</span>, <span style="color: #006080;">"Stephen J. Gould"</span>
<span style="color: #606060;"> 10:</span>     .Add <span style="color: #006080;">"The Da Vinci Code"</span>, <span style="color: #006080;">"Dan Brown"</span>
<span style="color: #606060;"> 11:</span>
<span style="color: #606060;"> 12:</span>     <span style="color: #008000;">\'Answer the question: who wrote "The Selfish Gene"?</span>
<span style="color: #606060;"> 13:</span>     strBook = <span style="color: #006080;">"The Selfish Gene"</span>
<span style="color: #606060;"> 14:</span>     MsgBox .item(strBook), vbOKOnly+vbInformation, <span style="color: #006080;">"Author query result for book: "</span> & strBook
<span style="color: #606060;"> 15:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>

The example above demonstrates the power of a dictionary as a data storage and data retrieval device. For instance, it eliminates the need for item search functions (direct access) and avoids duplicate key creation.

However, the dictionary can be much more than just a data storage and retrieval device. Thanks to its capacity to store variant data types, it is actually possible to store references to objects of different types. For instance, it is possible to store complex data structures by using nested dictionaries (this issue, however, is out of the scope of the current paper). In the next chapter, we shall delve into a method to exploit this feature of the scripting dictionary object to implement function pointers in VBScript.

Implementing Function Pointers in VB Script

Earlier in this article, we have shown that it is possible to encapsulate any function as a class by using the command wrapper design pattern. In addition, it has been indicated that the VBScript Scripting Dictionary object is equipped with the capacity of storing variant data types, including references to objects. In what follows a method will be described to combine these two features into a powerful design and programming technique.

Recall that a pointer is a variable that stores the memory address of a function or instantiated class object. Following the example given above in the Command Wrapper section, we shall now show how it is possible to build function pointers using a Scripting Dictionary. Here below, we shall demonstrate the case of the four basic arithmetic operations.

The Addition Operation

<span style="color: #606060;"> 1:</span> <span style="color: #0000ff;">Class</span> Sum
<span style="color: #606060;"> 2:</span>     <span style="color: #0000ff;">Private</span> m_arrVarNumbers
<span style="color: #606060;"> 3:</span>     <span style="color: #0000ff;">Private</span> m_varResult
<span style="color: #606060;"> 4:</span>
<span style="color: #606060;"> 5:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize()
<span style="color: #606060;"> 6:</span>         <span style="color: #008000;">\'Initialize the Numbers member as an empty array</span>
<span style="color: #606060;"> 7:</span>         <span style="color: #0000ff;">ReDim</span> m_arrVarNumbers(-1)
<span style="color: #606060;"> 8:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>
<span style="color: #606060;"> 9:</span>
<span style="color: #606060;"> 10:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> Init(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 11:</span>         Numbers = arrVarNumbers
<span style="color: #606060;"> 12:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 13:</span>
<span style="color: #606060;"> 14:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec()
<span style="color: #606060;"> 15:</span>         <span style="color: #0000ff;">Dim</span> ix, arrNumbers
<span style="color: #606060;"> 16:</span>
<span style="color: #606060;"> 17:</span>         <span style="color: #0000ff;">If</span> (<span style="color: #0000ff;">Not</span> IsArray(Numbers)) <span style="color: #0000ff;">Then</span> <span style="color: #008000;">\'Not an array, so nothing to do – exit function</span>
<span style="color: #606060;"> 18:</span>             <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 19:</span>             Exec = <span style="color: #006080;">"Invalid data type was supplied to perform the operation."</span>
<span style="color: #606060;"> 20:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 21:</span>         <span style="color: #0000ff;">ElseIf</span> (UBound(Numbers) - LBound(Numbers) + 1 <= 1) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 22:</span>             <span style="color: #008000;">\'Array is empty or has single item - Add your error handling code here</span>
<span style="color: #606060;"> 23:</span>             Exec = <span style="color: #006080;">"Not enough data was supplied to perform the operation."</span>
<span style="color: #606060;"> 24:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 25:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 26:</span>             arrNumbers = Numbers
<span style="color: #606060;"> 27:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 28:</span>
<span style="color: #606060;"> 29:</span>         Result = 0
<span style="color: #606060;"> 30:</span>         <span style="color: #0000ff;">For</span> ix = LBound(arrNumbers) <span style="color: #0000ff;">To</span> UBound(arrNumbers)
<span style="color: #606060;"> 31:</span>             <span style="color: #0000ff;">If</span> (IsNumeric(arrNumbers(ix))) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 32:</span>                 Result = Result + arrNumbers(ix)
<span style="color: #606060;"> 33:</span>             <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 34:</span>                 <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 35:</span>             <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 36:</span>         <span style="color: #0000ff;">Next</span>
<span style="color: #606060;"> 37:</span>         Exec = Result
<span style="color: #606060;"> 38:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 39:</span>
<span style="color: #606060;"> 40:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Numbers()
<span style="color: #606060;"> 41:</span>         Numbers = m_arrVarNumbers
<span style="color: #606060;"> 42:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 43:</span>
<span style="color: #606060;"> 44:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Numbers(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 45:</span>         m_arrVarNumbers = arrVarNumbers
<span style="color: #606060;"> 46:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 47:</span>
<span style="color: #606060;"> 48:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Result()
<span style="color: #606060;"> 49:</span>         Result = m_varResult
<span style="color: #606060;"> 50:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 51:</span>
<span style="color: #606060;"> 52:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Result(<span style="color: #0000ff;">ByVal</span> varResult)
<span style="color: #606060;"> 53:</span>         m_varResult = varResult
<span style="color: #606060;"> 54:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 55:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 56:</span>
<span style="color: #606060;"> 57:</span> <span style="color: #008000;">\'This function behaves as a constructor and returns an initialized instance of the class</span>
<span style="color: #606060;"> 58:</span> <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetSum(<span style="color: #0000ff;">ByVal</span> arrNumbers)
<span style="color: #606060;"> 59:</span>     <span style="color: #0000ff;">Set</span> GetSum = <span style="color: #0000ff;">New</span> Sum
<span style="color: #606060;"> 60:</span>     GetSum.Init(arrNumbers)
<span style="color: #606060;"> 61:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

 

The Subtraction Operation

<span style="color: #606060;"> 1:</span> <span style="color: #0000ff;">Class</span> Subtract
<span style="color: #606060;"> 2:</span>     <span style="color: #0000ff;">Private</span> m_arrVarNumbers
<span style="color: #606060;"> 3:</span>     <span style="color: #0000ff;">Private</span> m_varResult
<span style="color: #606060;"> 4:</span>
<span style="color: #606060;"> 5:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize()
<span style="color: #606060;"> 6:</span>         <span style="color: #008000;">\'Initialize the Numbers member as an empty array</span>
<span style="color: #606060;"> 7:</span>         <span style="color: #0000ff;">ReDim</span> m_arrVarNumbers(-1)
<span style="color: #606060;"> 8:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>
<span style="color: #606060;"> 9:</span>
<span style="color: #606060;"> 10:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> Init(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 11:</span>         Numbers = arrVarNumbers
<span style="color: #606060;"> 12:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 13:</span>
<span style="color: #606060;"> 14:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec()
<span style="color: #606060;"> 15:</span>         <span style="color: #0000ff;">Dim</span> ix, arrNumbers
<span style="color: #606060;"> 16:</span>
<span style="color: #606060;"> 17:</span>         <span style="color: #0000ff;">If</span> (<span style="color: #0000ff;">Not</span> IsArray(Numbers)) <span style="color: #0000ff;">Then</span> <span style="color: #008000;">\'Not an array, so nothing to do – exit function</span>
<span style="color: #606060;"> 18:</span>             <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 19:</span>             Exec = <span style="color: #006080;">"Invalid data type was supplied to perform the operation."</span>
<span style="color: #606060;"> 20:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 21:</span>         <span style="color: #0000ff;">ElseIf</span> (UBound(Numbers) - LBound(Numbers) + 1 <= 1) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 22:</span>             <span style="color: #008000;">\'Array is empty or has single item - Add your error handling code here</span>
<span style="color: #606060;"> 23:</span>             Exec = <span style="color: #006080;">"Not enough data was supplied to perform the operation."</span>
<span style="color: #606060;"> 24:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 25:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 26:</span>             arrNumbers = Numbers
<span style="color: #606060;"> 27:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 28:</span>
<span style="color: #606060;"> 29:</span>         Result = arrNumbers(LBound(arrNumbers))- arrNumbers(LBound(arrNumbers)+1)
<span style="color: #606060;"> 30:</span>         <span style="color: #0000ff;">For</span> ix = LBound(arrNumbers)+2 <span style="color: #0000ff;">To</span> UBound(arrNumbers)
<span style="color: #606060;"> 31:</span>             <span style="color: #0000ff;">If</span> (IsNumeric(arrNumbers(ix))) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 32:</span>                 Result = Result - arrNumbers(ix)
<span style="color: #606060;"> 33:</span>             <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 34:</span>                 <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 35:</span>             <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 36:</span>         <span style="color: #0000ff;">Next</span>
<span style="color: #606060;"> 37:</span>         Exec = Result
<span style="color: #606060;"> 38:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 39:</span>
<span style="color: #606060;"> 40:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Numbers()
<span style="color: #606060;"> 41:</span>         Numbers = m_arrVarNumbers
<span style="color: #606060;"> 42:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 43:</span>
<span style="color: #606060;"> 44:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Numbers(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 45:</span>         m_arrVarNumbers = arrVarNumbers
<span style="color: #606060;"> 46:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 47:</span>
<span style="color: #606060;"> 48:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Result()
<span style="color: #606060;"> 49:</span>         Result = m_varResult
<span style="color: #606060;"> 50:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 51:</span>
<span style="color: #606060;"> 52:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Result(<span style="color: #0000ff;">ByVal</span> varResult)
<span style="color: #606060;"> 53:</span>         m_varResult = varResult
<span style="color: #606060;"> 54:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 55:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 56:</span>
<span style="color: #606060;"> 57:</span> <span style="color: #008000;">\'This function behaves as a constructor and returns an initialized instance of the class</span>
<span style="color: #606060;"> 58:</span> <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetSubtract(<span style="color: #0000ff;">ByVal</span> arrNumbers)
<span style="color: #606060;"> 59:</span>     <span style="color: #0000ff;">Set</span> GetSubtract = <span style="color: #0000ff;">New</span> Subtract
<span style="color: #606060;"> 60:</span>     GetSubtract.Init(arrNumbers)
<span style="color: #606060;"> 61:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

 

The Multiplication Operation

<span style="color: #606060;"> 1:</span> <span style="color: #0000ff;">Class</span> Multiply
<span style="color: #606060;"> 2:</span>     <span style="color: #0000ff;">Private</span> m_arrVarNumbers
<span style="color: #606060;"> 3:</span>     <span style="color: #0000ff;">Private</span> m_varResult
<span style="color: #606060;"> 4:</span>
<span style="color: #606060;"> 5:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize()
<span style="color: #606060;"> 6:</span>         <span style="color: #008000;">\'Initialize the Numbers member as an empty array</span>
<span style="color: #606060;"> 7:</span>         <span style="color: #0000ff;">ReDim</span> m_arrVarNumbers(-1)
<span style="color: #606060;"> 8:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>
<span style="color: #606060;"> 9:</span>
<span style="color: #606060;"> 10:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> Init(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 11:</span>         Numbers = arrVarNumbers
<span style="color: #606060;"> 12:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 13:</span>
<span style="color: #606060;"> 14:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec()
<span style="color: #606060;"> 15:</span>         <span style="color: #0000ff;">Dim</span> ix, arrNumbers
<span style="color: #606060;"> 16:</span>
<span style="color: #606060;"> 17:</span>         <span style="color: #0000ff;">If</span> (<span style="color: #0000ff;">Not</span> IsArray(Numbers)) <span style="color: #0000ff;">Then</span> <span style="color: #008000;">\'Not an array, so nothing to do – exit function</span>
<span style="color: #606060;"> 18:</span>             <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 19:</span>             Exec = <span style="color: #006080;">"Invalid data type was supplied to perform the operation."</span>
<span style="color: #606060;"> 20:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 21:</span>         <span style="color: #0000ff;">ElseIf</span> (UBound(Numbers) - LBound(Numbers) + 1 <= 1) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 22:</span>             <span style="color: #008000;">\'Array is empty or has single item - Add your error handling code here</span>
<span style="color: #606060;"> 23:</span>             Exec = <span style="color: #006080;">"Not enough data was supplied to perform the operation."</span>
<span style="color: #606060;"> 24:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 25:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 26:</span>             arrNumbers = Numbers
<span style="color: #606060;"> 27:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 28:</span>
<span style="color: #606060;"> 29:</span>         Result = arrNumbers(LBound(arrNumbers)) * arrNumbers(LBound(arrNumbers)+1)
<span style="color: #606060;"> 30:</span>         <span style="color: #0000ff;">For</span> ix = LBound(arrNumbers)+2 <span style="color: #0000ff;">To</span> UBound(arrNumbers)
<span style="color: #606060;"> 31:</span>             <span style="color: #0000ff;">If</span> (IsNumeric(arrNumbers(ix))) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 32:</span>                 Result = Result * arrNumbers(ix)
<span style="color: #606060;"> 33:</span>             <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 34:</span>                 <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 35:</span>             <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 36:</span>         <span style="color: #0000ff;">Next</span>
<span style="color: #606060;"> 37:</span>         Exec = Result
<span style="color: #606060;"> 38:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 39:</span>
<span style="color: #606060;"> 40:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Numbers()
<span style="color: #606060;"> 41:</span>         Numbers = m_arrVarNumbers
<span style="color: #606060;"> 42:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 43:</span>
<span style="color: #606060;"> 44:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Numbers(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 45:</span>         m_arrVarNumbers = arrVarNumbers
<span style="color: #606060;"> 46:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 47:</span>
<span style="color: #606060;"> 48:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Result()
<span style="color: #606060;"> 49:</span>         Result = m_varResult
<span style="color: #606060;"> 50:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 51:</span>
<span style="color: #606060;"> 52:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Result(<span style="color: #0000ff;">ByVal</span> varResult)
<span style="color: #606060;"> 53:</span>         m_varResult = varResult
<span style="color: #606060;"> 54:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 55:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 56:</span>
<span style="color: #606060;"> 57:</span> <span style="color: #008000;">\'This function behaves as a constructor and returns an initialized instance of the class</span>
<span style="color: #606060;"> 58:</span> <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetMultiply(<span style="color: #0000ff;">ByVal</span> arrNumbers)
<span style="color: #606060;"> 59:</span>     <span style="color: #0000ff;">Set</span> GetMultiply = <span style="color: #0000ff;">New</span> Multiply
<span style="color: #606060;"> 60:</span>     GetMultiply.Init(arrNumbers)
<span style="color: #606060;"> 61:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

 

The Division Operation

<span style="color: #606060;"> 1:</span> <span style="color: #0000ff;">Class</span> Divide
<span style="color: #606060;"> 2:</span>     <span style="color: #0000ff;">Private</span> m_arrVarNumbers
<span style="color: #606060;"> 3:</span>     <span style="color: #0000ff;">Private</span> m_varResult
<span style="color: #606060;"> 4:</span>
<span style="color: #606060;"> 5:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize()
<span style="color: #606060;"> 6:</span>         <span style="color: #008000;">\'Initialize the Numbers member as an empty array</span>
<span style="color: #606060;"> 7:</span>         <span style="color: #0000ff;">ReDim</span> m_arrVarNumbers(-1)
<span style="color: #606060;"> 8:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>
<span style="color: #606060;"> 9:</span>
<span style="color: #606060;"> 10:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> Init(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 11:</span>         Numbers = arrVarNumbers
<span style="color: #606060;"> 12:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 13:</span>
<span style="color: #606060;"> 14:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec()
<span style="color: #606060;"> 15:</span>         <span style="color: #0000ff;">Dim</span> ix, arrNumbers
<span style="color: #606060;"> 16:</span>
<span style="color: #606060;"> 17:</span>         <span style="color: #0000ff;">If</span> (<span style="color: #0000ff;">Not</span> IsArray(Numbers)) <span style="color: #0000ff;">Then</span> <span style="color: #008000;">\'Not an array, so nothing to do – exit function</span>
<span style="color: #606060;"> 18:</span>             <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 19:</span>             Exec = <span style="color: #006080;">"Invalid data type was supplied to perform the operation."</span>
<span style="color: #606060;"> 20:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 21:</span>         <span style="color: #0000ff;">ElseIf</span> (UBound(Numbers) - LBound(Numbers) + 1 <= 1) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 22:</span>             <span style="color: #008000;">\'Array is empty or has single item - Add your error handling code here</span>
<span style="color: #606060;"> 23:</span>             Exec = <span style="color: #006080;">"Not enough data was supplied to perform the operation."</span>
<span style="color: #606060;"> 24:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 25:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 26:</span>             arrNumbers = Numbers
<span style="color: #606060;"> 27:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 28:</span>
<span style="color: #606060;"> 29:</span>         <span style="color: #0000ff;">If</span> (IsNumeric(arrNumbers(LBound(arrNumbers))) <span style="color: #0000ff;">And</span> IsNumeric(arrNumbers(LBound(arrNumbers)+1)) <span style="color: #0000ff;">And</span> (arrNumbers(LBound(arrNumbers)+1) <> 0)) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 30:</span>             Result = arrNumbers(LBound(arrNumbers)) / arrNumbers(LBound(arrNumbers)+1)
<span style="color: #606060;"> 31:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 32:</span>             <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 33:</span>             Exec = <span style="color: #006080;">"Invalid data was supplied to perform the operation."</span>
<span style="color: #606060;"> 34:</span>             <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 35:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 36:</span>
<span style="color: #606060;"> 37:</span>         <span style="color: #0000ff;">For</span> ix = LBound(arrNumbers)+2 <span style="color: #0000ff;">To</span> UBound(arrNumbers)
<span style="color: #606060;"> 38:</span>             <span style="color: #0000ff;">If</span> (IsNumeric(arrNumbers(ix)) <span style="color: #0000ff;">And</span> (arrNumbers(ix) <> 0)) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 39:</span>                 Result = Result / arrNumbers(ix)
<span style="color: #606060;"> 40:</span>             <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 41:</span>                 <span style="color: #008000;">\'Add your error handling code here</span>
<span style="color: #606060;"> 42:</span>             <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 43:</span>         <span style="color: #0000ff;">Next</span>
<span style="color: #606060;"> 44:</span>         Exec = Result
<span style="color: #606060;"> 45:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 46:</span>
<span style="color: #606060;"> 47:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Numbers()
<span style="color: #606060;"> 48:</span>         Numbers = m_arrVarNumbers
<span style="color: #606060;"> 49:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 50:</span>
<span style="color: #606060;"> 51:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Numbers(<span style="color: #0000ff;">ByVal</span> arrVarNumbers)
<span style="color: #606060;"> 52:</span>         m_arrVarNumbers = arrVarNumbers
<span style="color: #606060;"> 53:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 54:</span>
<span style="color: #606060;"> 55:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Get</span> Result()
<span style="color: #606060;"> 56:</span>         Result = m_varResult
<span style="color: #606060;"> 57:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 58:</span>
<span style="color: #606060;"> 59:</span>     <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Property</span> <span style="color: #0000ff;">Let</span> Result(<span style="color: #0000ff;">ByVal</span> varResult)
<span style="color: #606060;"> 60:</span>         m_varResult = varResult
<span style="color: #606060;"> 61:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Property</span>
<span style="color: #606060;"> 62:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 63:</span>
<span style="color: #606060;"> 64:</span> <span style="color: #008000;">\'This function behaves as a constructor and returns an initialized instance of the class</span>
<span style="color: #606060;"> 65:</span> <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetDivide(<span style="color: #0000ff;">ByVal</span> arrNumbers)
<span style="color: #606060;"> 66:</span>     <span style="color: #0000ff;">Set</span> GetDivide = <span style="color: #0000ff;">New</span> Divide
<span style="color: #606060;"> 67:</span>     GetDivide.Init(arrNumbers)
<span style="color: #606060;"> 68:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

 

Using the Function Pointers to Perform the basic Arithmetic Operations

<span style="color: #606060;"> 1:</span> <span style="color: #0000ff;">Dim</span> dicFunctionHandler
<span style="color: #606060;"> 2:</span>
<span style="color: #606060;"> 3:</span> <span style="color: #008000;">\'Create an instance of the scripting dictionary class</span>
<span style="color: #606060;"> 4:</span> <span style="color: #0000ff;">Set</span> dicFunctionHandler = CreateObject(<span style="color: #006080;">"Scripting.Dictionary"</span>)
<span style="color: #606060;"> 5:</span>
<span style="color: #606060;"> 6:</span> <span style="color: #008000;">\'Load some functions</span>
<span style="color: #606060;"> 7:</span> <span style="color: #0000ff;">With</span> dicFunctionHandler
<span style="color: #606060;"> 8:</span>     .Add <span style="color: #006080;">"+"</span>, <span style="color: #0000ff;">New</span> Sum
<span style="color: #606060;"> 9:</span>     .Add <span style="color: #006080;">"-"</span>, <span style="color: #0000ff;">New</span> Subtract
<span style="color: #606060;"> 10:</span>     .Add <span style="color: #006080;">"*"</span>, <span style="color: #0000ff;">New</span> Multiply
<span style="color: #606060;"> 11:</span>     .Add <span style="color: #006080;">"/"</span>, <span style="color: #0000ff;">New</span> Divide
<span style="color: #606060;"> 12:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>
<span style="color: #606060;"> 13:</span>
<span style="color: #606060;"> 14:</span> <span style="color: #008000;">\'Execute the functions using the Function Handler</span>
<span style="color: #606060;"> 15:</span> <span style="color: #0000ff;">With</span> dicFunctionHandler
<span style="color: #606060;"> 16:</span>     <span style="color: #0000ff;">With</span> .item(<span style="color: #006080;">"+"</span>)
<span style="color: #606060;"> 17:</span>         <span style="color: #0000ff;">Call</span> .Init(Array(23, 56, 78, 95, 114))
<span style="color: #606060;"> 18:</span>         MsgBox .Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (+)"</span> <span style="color: #008000;">\'Display result returned by the Exec method</span>
<span style="color: #606060;"> 19:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>
<span style="color: #606060;"> 20:</span>     <span style="color: #0000ff;">With</span> .item(<span style="color: #006080;">"-"</span>)
<span style="color: #606060;"> 21:</span>         <span style="color: #0000ff;">Call</span> .Init(Array(117, 23))
<span style="color: #606060;"> 22:</span>         MsgBox .Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (-)"</span> <span style="color: #008000;">\'Display result returned by the Exec method</span>
<span style="color: #606060;"> 23:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>
<span style="color: #606060;"> 24:</span>     <span style="color: #0000ff;">With</span> .item(<span style="color: #006080;">"*"</span>)
<span style="color: #606060;"> 25:</span>             <span style="color: #0000ff;">Call</span> .Init(Array(7, 5))
<span style="color: #606060;"> 26:</span>             MsgBox .Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (*)"</span> <span style="color: #008000;">\'Display result returned by the Exec method</span>
<span style="color: #606060;"> 27:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>
<span style="color: #606060;"> 28:</span>     <span style="color: #0000ff;">With</span> .item(<span style="color: #006080;">"/"</span>)
<span style="color: #606060;"> 29:</span>         <span style="color: #0000ff;">Call</span> .Init(Array(84, 12))
<span style="color: #606060;"> 30:</span>         MsgBox .Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (/)"</span> <span style="color: #008000;">\'Display result returned by the Exec method</span>
<span style="color: #606060;"> 31:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>
<span style="color: #606060;"> 32:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>
<span style="color: #606060;"> 33:</span>
<span style="color: #606060;"> 34:</span> <span style="color: #008000;">\'Or, using the "constructors":</span>
<span style="color: #606060;"> 35:</span> <span style="color: #008000;">\'First, load some functions</span>
<span style="color: #606060;"> 36:</span> <span style="color: #0000ff;">With</span> dicFunctionHandler
<span style="color: #606060;"> 37:</span>     .RemoveAll
<span style="color: #606060;"> 38:</span>     .Add <span style="color: #006080;">"+"</span>, GetSum(Array(23, 56, 78, 95, 114))
<span style="color: #606060;"> 39:</span>     .Add <span style="color: #006080;">"-"</span>, GetSubtract(Array(117, 23))
<span style="color: #606060;"> 40:</span>     .Add <span style="color: #006080;">"*"</span>, GetMultiply(Array(7, 5))
<span style="color: #606060;"> 41:</span>     .Add <span style="color: #006080;">"/"</span>, GetDivide(Array(84, 12))
<span style="color: #606060;"> 42:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>
<span style="color: #606060;"> 43:</span>
<span style="color: #606060;"> 44:</span> <span style="color: #008000;">\'Second, execute the functions using the Function Handler</span>
<span style="color: #606060;"> 45:</span> <span style="color: #0000ff;">With</span> dicFunctionHandler
<span style="color: #606060;"> 46:</span>     MsgBox .item(<span style="color: #006080;">"+"</span>).Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (+)"</span> <span style="color: #008000;">\'Display result returned by the Exec method</span>
<span style="color: #606060;"> 47:</span>     MsgBox .item(<span style="color: #006080;">"-"</span>).Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (-)"</span> <span style="color: #008000;">\'Display result returned by the Exec method</span>
<span style="color: #606060;"> 48:</span>     MsgBox .item(<span style="color: #006080;">"*"</span>).Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (*)"</span> <span style="color: #008000;">\'Display result returned by the Exec method </span>
<span style="color: #606060;"> 49:</span>     MsgBox .item(<span style="color: #006080;">"/"</span>).Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (/)"</span> <span style="color: #008000;">\'Display result returned by the Exec method</span>
<span style="color: #606060;"> 50:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>

In the above example we have shown how to:

  1. Implement the Command Wrapper design pattern in VBScript;
  2. Implement a “constructor” for a class in VBScript;
  3. Implement a function handler using a scripting dictionary;
  4. Instantiate such custom classes and load them to a scripting dictionary;
  5. Call the loaded function via the dictionary key and retrieve the result.

This is the method suggested in this paper to implement a function pointer in VBScript.

There is also another possible way to call a function implemented with this method, as follows:

<span style="color: #606060;"> 1:</span> <span style="color: #008000;">\'Execute the functions using the Function Handler</span>
<span style="color: #606060;"> 2:</span> MsgBox dicFunctionHandler(“+”).Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (+)"</span>
<span style="color: #606060;"> 3:</span> MsgBox dicFunctionHandler(“-”).Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (-)"</span>
<span style="color: #606060;"> 4:</span> MsgBox dicFunctionHandler(“*”).Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (*)"</span>
<span style="color: #606060;"> 5:</span> MsgBox dicFunctionHandler(“/”).Exec(), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (/)"</span>

and this is because the item property is the scripting dictionary’s default property.

In a similar fashion, it is possible to define the Exec methods of the above mentioned classes as Default (by declaring it: Public Default Function) and then the code above can be further reduced to:

<span style="color: #606060;"> 1:</span> <span style="color: #008000;">\'Execute the functions using the Function Handler</span>
<span style="color: #606060;"> 2:</span> MsgBox dicFunctionHandler(“+”), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (+)"</span>
<span style="color: #606060;"> 3:</span> MsgBox dicFunctionHandler(“-”), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (-)"</span>
<span style="color: #606060;"> 4:</span> MsgBox dicFunctionHandler(“*”), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (*)"</span>
<span style="color: #606060;"> 5:</span> MsgBox dicFunctionHandler(“/”), vbOKOnly+vbInformation, <span style="color: #006080;">"Result (/)"</span>
<span style="color: #606060;"> 6:</span>

The readers are encouraged to try to execute the sample code shown in this paper, as well as to try the method to implement their own functionality.

 

Performance of Function Pointers

A small case study comparing the performance of regular function calls with that of function pointers shows that the more a function is used, the more worthwhile it is to use function pointers.

Number of Calls Single 10 20 50 100
Regular calls 0.0943 0.5630 1.0635 2.5806 5.0674
Function Pointers 0.1561 0.5308 0.9212 2.0922 4.1219

 

Discussion

We have seen so far how to implement the Command Wrapper design pattern in VBScript and a “constructor” for a class in VBScript, as well as a function handler using a scripting dictionary. We have also shown how to instantiate such custom classes and load them to a scripting dictionary, together with different ways to call the loaded function via the dictionary key and to retrieve the result. We have also indicated that this method is, in fact, equivalent to the implementation of a function pointer in C or C++.

The general uses and benefits of function pointers are explained elsewhere (e.g., Lars Haendel, 2005), and hence they will not be covered here. In what remains I shall attempt to convey in which cases the implementation of this design pattern in VBScript in general, and with Quicktest Pro (QTP) in particular, might be of benefit.

First, the method presented in this paper should be of great value when the basic hardware configuration is poor, i.e., when the system is low in RAM, by means of dynamic loading of code blocks. Recall that common QTP usage requires the automation developer to add every single function library to the test resources.

Second, by implementing a function handler as illustrated above, it would be possible to build a generic controller component that would enable execution of real keyword-driven scripts. With such a component it would be possible, for example, to define the flow of the different code blocks in an external file, such as an XML file.

Third, the method can be used to emulate callbacks, which is a central feature of function pointers. This can be easily done by passing the function handler entry (dictionary item) to another function.

Fourth, the method can be used to emulate event handling. This can be achieved by returning a string with the name of the function to be called. Please notice that this technique would yield a highly parsimonious coding style, for it makes the need for decision structures to analyze the return code of a function obsolete. An example for this can be found in Appendix 2.

Conclusion

This paper attempted to demonstrate how to implement function pointers in VBScript, and pinpointed the possible uses and advantages of the method in general and particularly for QTP. It is concluded that the technique can help developers to achieve more efficient and generic design and code, and better run-time resources management. Future forthcoming articles will further expand on several topics mentioned throughout this paper.

References

Haendel, L. (2005). The Function Pointer Tutorials. Introduction to C and C++ Function Pointers, Callbacks and Functors. Source: http://www.newty.de/fpt/zip/e_fpt.pdf

Gamma, E., Helm, R., Johnson, R., & Vlissides, J. (1997). Design Patterns: Elements of Reusable Object Oriented Software. Addison-Wesley Publishing.

Appendix 1: Event Handling Example

We shall show here how to implement an event handler in VBScript for a typical login flow in QTP. The flow includes three functions: Login, FirstTest and Logout. In addition, two additional functions (AbortTest and Cleanup) are included for error handling and orderly exiting the test flow. Within the TestFlow Sub, the functions are loaded using a generic function (class) loader function (GetFunction), and then called according to their order in the arrRunActions, with their corresponding arguments from arrActionArgs. As can be seen in the code, in case of error the Login.Exec function returns the name of the action to be performed (AbortTest). This action is then performed by means of the statement: strNextAction = dicFunctionHandler(strNextAction)(arrArgs), and since the for loop continues to advance, the normal course of the test flow is altered such that the FirstTest action is never executed.

 

<span style="color: #606060;"> 1:</span> <span style="color: #0000ff;">Class</span> Login
<span style="color: #606060;"> 2:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec(<span style="color: #0000ff;">ByVal</span> arrArgs)
<span style="color: #606060;"> 3:</span>         <span style="color: #008000;">\'Add your code here</span>
<span style="color: #606060;"> 4:</span>         <span style="color: #0000ff;">If</span> (<span style="color: #0000ff;">Not</span> IsArray(arrArgs)) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 5:</span>             MsgBox <span style="color: #006080;">"Error: username & password are mandatory parameters."</span>, _
<span style="color: #606060;"> 6:</span>                     vbOKOnly+vbCritical, <span style="color: #006080;">"Login"</span>
<span style="color: #606060;"> 7:</span>             Exec = <span style="color: #006080;">"AbortTest"</span>
<span style="color: #606060;"> 8:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 9:</span>             MsgBox Join(arrArgs, <span style="color: #006080;">";"</span>), vbOKOnly+vbInformation, <span style="color: #006080;">"Login"</span>
<span style="color: #606060;"> 10:</span>             Exec = <span style="color: #006080;">""</span>
<span style="color: #606060;"> 11:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 12:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 13:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 14:</span>
<span style="color: #606060;"> 15:</span> <span style="color: #0000ff;">Class</span> Logout
<span style="color: #606060;"> 16:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec(<span style="color: #0000ff;">ByVal</span> arrArgs)
<span style="color: #606060;"> 17:</span>         MsgBox <span style="color: #006080;">"Exec"</span>, vbOKOnly+vbInformation, <span style="color: #006080;">"Logout"</span>
<span style="color: #606060;"> 18:</span>         <span style="color: #008000;">\'Add your code here</span>
<span style="color: #606060;"> 19:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 20:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 21:</span>
<span style="color: #606060;"> 22:</span> <span style="color: #0000ff;">Class</span> AbortTest
<span style="color: #606060;"> 23:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec(<span style="color: #0000ff;">ByVal</span> arrArgs)
<span style="color: #606060;"> 24:</span>         MsgBox <span style="color: #006080;">"Exec"</span>, vbOKOnly+vbCritical, <span style="color: #006080;">"AbortTest"</span>
<span style="color: #606060;"> 25:</span>         Exec = <span style="color: #006080;">"Cleanup"</span>
<span style="color: #606060;"> 26:</span>         <span style="color: #008000;">\'Add your code here</span>
<span style="color: #606060;"> 27:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 28:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 29:</span>
<span style="color: #606060;"> 30:</span> <span style="color: #0000ff;">Class</span> FirstTest
<span style="color: #606060;"> 31:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec(<span style="color: #0000ff;">ByVal</span> arrArgs)
<span style="color: #606060;"> 32:</span>         MsgBox <span style="color: #006080;">"Exec"</span>, vbOKOnly+vbInformation, <span style="color: #006080;">"FirstTest"</span>
<span style="color: #606060;"> 33:</span>         <span style="color: #008000;">\'Add your code here</span>
<span style="color: #606060;"> 34:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 35:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 36:</span>
<span style="color: #606060;"> 37:</span> <span style="color: #0000ff;">Class</span> Cleanup
<span style="color: #606060;"> 38:</span>     <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Default</span> <span style="color: #0000ff;">Function</span> Exec(<span style="color: #0000ff;">ByVal</span> arrArgs)
<span style="color: #606060;"> 39:</span>         MsgBox <span style="color: #006080;">"Exec"</span>, vbOKOnly+vbInformation, <span style="color: #006080;">"Cleanup"</span>
<span style="color: #606060;"> 40:</span>         <span style="color: #008000;">\'Add your code here</span>
<span style="color: #606060;"> 41:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 42:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>
<span style="color: #606060;"> 43:</span>
<span style="color: #606060;"> 44:</span> <span style="color: #008000;">\'This generic function will load any class</span>
<span style="color: #606060;"> 45:</span> <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetFunction(<span style="color: #0000ff;">ByVal</span> strFunctionName)
<span style="color: #606060;"> 46:</span>     Execute <span style="color: #006080;">"Set GetFunction = New "</span> & strFunctionName
<span style="color: #606060;"> 47:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>
<span style="color: #606060;"> 48:</span>
<span style="color: #606060;"> 49:</span> <span style="color: #008000;">\'This sub will run the flow</span>
<span style="color: #606060;"> 50:</span> <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> TestFlow(<span style="color: #0000ff;">ByVal</span> arrLoadActions, <span style="color: #0000ff;">ByVal</span> arrRunActions, <span style="color: #0000ff;">ByVal</span> arrActionArgs)
<span style="color: #606060;"> 51:</span>     <span style="color: #0000ff;">Dim</span> dicFunctionHandler, ix, arrArgs, strNextAction
<span style="color: #606060;"> 52:</span>
<span style="color: #606060;"> 53:</span>     <span style="color: #0000ff;">Set</span> dicFunctionHandler = CreateObject(<span style="color: #006080;">"Scripting.Dictionary"</span>)
<span style="color: #606060;"> 54:</span>     <span style="color: #0000ff;">Set</span> arrArgs = CreateObject(<span style="color: #006080;">"Scripting.Dictionary"</span>)
<span style="color: #606060;"> 55:</span>
<span style="color: #606060;"> 56:</span>     <span style="color: #008000;">\'Load the required functions</span>
<span style="color: #606060;"> 57:</span>     <span style="color: #0000ff;">With</span> dicFunctionHandler
<span style="color: #606060;"> 58:</span>         <span style="color: #0000ff;">For</span> ix = LBound(arrLoadActions) <span style="color: #0000ff;">To</span> UBound(arrLoadActions)
<span style="color: #606060;"> 59:</span>             .Add arrLoadActions (ix), GetFunction(arrLoadActions (ix))
<span style="color: #606060;"> 60:</span>         <span style="color: #0000ff;">Next</span>
<span style="color: #606060;"> 61:</span>     <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">With</span>
<span style="color: #606060;"> 62:</span>
<span style="color: #606060;"> 63:</span>     <span style="color: #008000;">\'Run the required flow</span>
<span style="color: #606060;"> 64:</span>     strNextAction = <span style="color: #006080;">""</span>
<span style="color: #606060;"> 65:</span>     <span style="color: #0000ff;">For</span> ix = LBound(arrRunActions) <span style="color: #0000ff;">To</span> UBound(arrRunActions)
<span style="color: #606060;"> 66:</span>         <span style="color: #008000;">\'Get the action arguments</span>
<span style="color: #606060;"> 67:</span>         arrArgs = split(arrActionArgs(ix), <span style="color: #006080;">";"</span>)
<span style="color: #606060;"> 68:</span>         <span style="color: #0000ff;">If</span> (UBound(arrArgs) - LBound(arrArgs) + 1 = 0) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 69:</span>             <span style="color: #008000;">\'If no args are found, pass an empty string</span>
<span style="color: #606060;"> 70:</span>             arrArgs = <span style="color: #006080;">""</span>
<span style="color: #606060;"> 71:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 72:</span>         <span style="color: #008000;">\'If the next command is empty</span>
<span style="color: #606060;"> 73:</span>         <span style="color: #0000ff;">If</span> (strNextAction = <span style="color: #006080;">""</span>) <span style="color: #0000ff;">Then</span>
<span style="color: #606060;"> 74:</span>             <span style="color: #008000;">\'Run the next planned action</span>
<span style="color: #606060;"> 75:</span>             strNextAction = dicFunctionHandler(arrRunActions(ix))(arrArgs)
<span style="color: #606060;"> 76:</span>         <span style="color: #0000ff;">Else</span>
<span style="color: #606060;"> 77:</span>             <span style="color: #008000;">\'Run the action returned by the previously call action</span>
<span style="color: #606060;"> 78:</span>             strNextAction = dicFunctionHandler(strNextAction)(arrArgs)
<span style="color: #606060;"> 79:</span>         <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #606060;"> 80:</span>     <span style="color: #0000ff;">Next</span>
<span style="color: #606060;"> 81:</span> <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>
<span style="color: #606060;"> 82:</span>
<span style="color: #606060;"> 83:</span> <span style="color: #0000ff;">Call</span> TestFlow(Array(<span style="color: #006080;">"Login"</span>, <span style="color: #006080;">"FirstTest"</span>, <span style="color: #006080;">"Logout"</span>, <span style="color: #006080;">"AbortTest"</span>, <span style="color: #006080;">"Cleanup"</span>), _
<span style="color: #606060;"> 84:</span>               Array(<span style="color: #006080;">"Login"</span>, <span style="color: #006080;">"FirstTest"</span>, <span style="color: #006080;">"Logout"</span>), Array(<span style="color: #006080;">"User;123456"</span>, <span style="color: #006080;">""</span>, <span style="color: #006080;">""</span>))

Appendix 2: Implementing a Callback with Function Pointers

We shall show here how to implement a callback with the classes defined in Appendix 1 and used in this paper. The example used here will be the calculation of the total amount plus the tax on the sum of several items prices.

<span style="color: #606060;"> 1:</span> <span style="color: #008000;">\'Define a constant with the tax value multiplier</span>
<span style="color: #606060;"> 2:</span> <span style="color: #0000ff;">Const</span> CONST_DBL_VAT = 1.155
<span style="color: #606060;"> 3:</span>
<span style="color: #606060;"> 4:</span> <span style="color: #008000;">\'Now, by dynamically loading the functions without the function handler (as shown earlier in this article):</span>
<span style="color: #606060;"> 5:</span> <span style="color: #008000;">\'Execute the (*) function on the result of the (+) function to calculate the tax on an item</span>
<span style="color: #606060;"> 6:</span> MsgBox GetMultiply(Array(GetSum(Array(23, 56, 78, 95, 114)), CONST_DBL_VAT)), _
<span style="color: #606060;"> 7:</span>                 vbOKOnly, <span style="color: #006080;">"Result (Tax)"</span>
<span style="color: #606060;"> 8:</span>

True, the syntax looks complicated, but it is very useful.

, , , , , , , , ,

Factory Pattern

0
by on April 1, 2008 at 09:56

This article refers to design patterns, pointers and objects (discussed in the background section of this article), and classes. Make sure you’re familiar with these issues before reading on. It would also be prudent to brush up on the Singleton Pattern.

We continue our tour of Design Patterns with another basic pattern – the Factory (or Abstract Factory). Like the Singleton Pattern, the Factory Pattern is a creational pattern, which means it is used to create or initialize a certain object. As you might have understood by its name, this pattern’s implementation can act as a factory, which takes in manufacturing instructions, and spits out the relevant product (or object).

As with all the design patterns we will cover, this too will be mutated to fit into the world of VBScript and QTP. The solution I’ll present and its implementations will be a synthesis of the Factory method, the Abstract Factory method, and some VBScript class techniques. So if you’re familiar with Factory/Abstract Factory solutions from object-oriented languages, prepare to see something new.

A Little Help from a Genie

The simplest implementation of the Factory Pattern is the Genie. This is because much like a Genie, we just have to tell it what object we need, and it will magically conjure it up. A custom object repository provides a solid example. It will combine the benefits of “naming” objects (like the QTP native OR), with the benefits of using Descriptive Programming.

We could easily construct such an object repository with an “Object Genie”, as discussed in a previous article and originally presented here. The Genie will serve us the relevant object corresponding to the logical name we provide it.

<span style="color: #0000ff;">Function</span> ObjectGenie(sObjectName)
    <span style="color: #0000ff;">Select</span> <span style="color: #0000ff;">Case</span> sObjectName
        <span style="color: #0000ff;">Case</span> <span style="color: #006080;">"Save Button"</span>
            <span style="color: #0000ff;">Set</span> ObjectGenie = VBWindow(<span style="color: #006080;">"vbname:=X"</span>).VBButton(<span style="color: #006080;">"text:=Save"</span>)
        <span style="color: #0000ff;">Case</span> <span style="color: #006080;">"Main Window"</span>
            <span style="color: #0000ff;">Set</span> ObjectGenie = VBWindow(<span style="color: #006080;">"vbname:=X"</span>)
        <span style="color: #0000ff;">Case</span> <span style="color: #0000ff;">Else</span>
            <span style="color: #0000ff;">Set</span> ObjectGenie = <span style="color: #0000ff;">Nothing</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Select</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

ObjectGenie(<span style="color: #006080;">"Save Button"</span>).Click \'Clicks the save button

This is a very naïve and simplified object Genie, and a real one would probably include delegation to several sub-functions, building the object only at the end of the function, and wrapping it with a meta-data shell. Though it’s no required in order to understand the Factory Pattern, a more complex example for an object Genie implementation is available here, if you’re interested.

As we’ve seen, the object Genie takes in manufacturing instructions (e.g. the object logical name), and spits out the corresponding product (e.g. the actual QTP object).  A lot of the everyday mechanisms in QTP and VBScript are in fact implementations of the Object Genie principle – and by extension of the Factory Pattern. If we think about it, the native QTP OR act very much like our custom object Genie (though it’s far more elaborate); and the ever popular CreateObject command embodies the very essence of the Genie principle.

<span style="color: #0000ff;">Set</span> oWord = CreateObject(<span style="color: #006080;">"Word.Application"</span>)

Here CreateObject is the Object Genie / Factory, “Word.Application” is the manufacturing instruction, and the oWord object is the final product. For all its directness and simplicity, the CreateObject command is an extremely flexible factory implementation, as it can create all the COM objects known in your system.

The Benefits of Remembrance

These Genie examples have some virtues – they are easy to implement and maintain, and provide a central access point for multiple object creation. However, they have one major drawback – they don’t remember the objects they’ve created. This is exactly the problem that the Singleton Pattern was set to solve, but it was built to manage the resources of only a single object.

Let’s see if we can combine the multiple object creation capabilities of the Factory Pattern, with the object resource management capabilities of the Singleton Pattern.

We’ve seen how a single excel object is managed via the Singleton Pattern. Now, say we don’t just want to write our data to one excel file, but three; or maybe a combination of excel file and text files; or some files and some DB connections. While these objects differ in detail, they are similar in the logical role they play in our script – outputting data. If we just wrap each of them in a Singleton class, or use a different global variable for each of them, we miss some of this logical similarity between them.

It seems that we need a central authority to create, store and manage these output mechanisms. This can be easily constructed using a Scripting.Dictionary storage with a class wrapper. The class will have a .Construct method (the Object Genie), which will create a new output channel of the requested type, and add it to the storage with a unique identifier. The new channel can be recalled later using the identifier. You could say it’s the old object Genie, with a new twist.

<span style="color: #008000;">\'Provide a global access point</span>
<span style="color: #0000ff;">Public</span> oOutputs

<span style="color: #0000ff;">Set</span> oOutputs = <span style="color: #0000ff;">New</span> clsOutputFactory

<span style="color: #0000ff;">Class</span> clsOutputFactory
    <span style="color: #0000ff;">Public</span> Channels <span style="color: #008000;">\'Stores the output channel</span>

    <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize
        <span style="color: #0000ff;">Set</span> <span style="color: #0000ff;">Me</span>.Channels = CreateObject(<span style="color: #006080;">"Scripting.Dictionary"</span>)
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Terminate
        <span style="color: #0000ff;">Set</span> <span style="color: #0000ff;">Me</span>.Channels = <span style="color: #0000ff;">Nothing</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> Construct (sChannelName, sChannelType)
        <span style="color: #0000ff;">Dim</span> oNewChannel

        <span style="color: #0000ff;">Select</span> <span style="color: #0000ff;">Case</span> sChannelType
            <span style="color: #0000ff;">Case</span> <span style="color: #006080;">"Excel"</span>
                <span style="color: #008000;">\'Create a new excel channel class</span>
                <span style="color: #0000ff;">Set</span> oNewChannel = <span style="color: #0000ff;">New</span> ExcelChannelClass
            <span style="color: #0000ff;">Case</span> <span style="color: #006080;">"Text"</span>
                <span style="color: #008000;">\'Text init code here</span>
            <span style="color: #0000ff;">Case</span> <span style="color: #006080;">"DB"</span>
                <span style="color: #008000;">\'DB init code here</span>
        <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Select</span>

        <span style="color: #008000;">\'Add the new channel to the storage</span>
        <span style="color: #0000ff;">Me</span>.Channels.Add sChannelName, oNewChannel
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

<span style="color: #0000ff;">End</span> Class

And now we can add the required channels at the beginning of our script

oOutputs.Construct <span style="color: #006080;">"Errors"</span>, <span style="color: #006080;">"Excel"</span>

oOutputs.Construct <span style="color: #006080;">"Backup"</span>, <span style="color: #006080;">"Text"</span>

oOutputs.Construct <span style="color: #006080;">"Another output log"</span>, <span style="color: #006080;">"Text"</span>

And use them throughout our script

<span style="color: #008000;">\'Imagine our excel class wrapper has a .Write method</span>
oOutputs.Channels(<span style="color: #006080;">"Errors"</span>).Write <span style="color: #006080;">"Something"</span>

This mechanism achieves several goals. It puts an end to the global variable inflation – instead of dozens separate global variables, we only need the one to access our channel storage; Moreover, the number of output channels is dynamic, and could change from script to script and over time (with zero maintenance).  It centralizes the object creation process to one main Factory, which serves all the output needs for the script.

One major benefit of centralizing our object creation and remembering what we’ve create is the ability to add layers of validation and business logic to the process. For instance, we could check to see if the requested channel was already created, and act accordingly:

<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> Construct (sChannelName, sChannelType)
    <span style="color: #0000ff;">Dim</span> oNewChannel

    <span style="color: #0000ff;">If</span> <span style="color: #0000ff;">Me</span>.Channels.Exist(sChannelName) <span style="color: #0000ff;">Then</span> <span style="color: #0000ff;">Exit</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #0000ff;">Select</span> <span style="color: #0000ff;">Case</span> sChannelType
        <span style="color: #008000;">\'Continue code…</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

Of course this is a very basic validation – usually we would implement a much more sophisticated mechanism. For example, we can add a validation of the file’s name / DB table name to make sure you don’t overwrite our own outputs. We can pool our DB connections in a much more sophisticated and efficient manner than the one made possible by using only the Singleton Pattern. The possibilities are endless.

Centralizing Global Actions

We’ve just seen how a centralize storage can offer plenty of opportunities to make our code tidier and more efficient. However, there is more to come – by remembering the objects we’ve created, we can perform global actions on all of them at once. For example, we may want to output some data through all our channels – text, excel and DB.

One way of doing that is to separately send the output to each and every channel, which can become quite a headache once the number of channels will inflate. Another way is to add an .OutputAll method to our storage, which will loop through all the channels that factory has created, and send the output to each and every one of them.

<span style="color: #0000ff;">Sub</span> OutputAll(sData)
    <span style="color: #0000ff;">Dim</span> arrKeys

    arrKeys = <span style="color: #0000ff;">Me</span>.Channels.Keys

    <span style="color: #0000ff;">For</span> I = 0 <span style="color: #0000ff;">To</span> <span style="color: #0000ff;">Me</span>.Channels.Count – 1
        <span style="color: #0000ff;">Me</span>.Channels(arrKeys(i)).Write sData
    <span style="color: #0000ff;">Next</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

While this technique is not directly related to the pure Factory Pattern, the only way to implement it in VBScript is by coupling it with the object creation process controlled by the Factory. Of course that much like the possibility to apply validations during the object’s creation (Singleton validations or others), we can apply validations for our global commands. For instance, we can choose to output the data only to excel channels, only to channels that output to files on drive C, or only to channels which have to word “Error” in their logical name.

Assigning Responsibilities and Polymorphism

In order for our global commands to work, we must make sure that all the objects we’ve created support these methods and sub-procedures. So for example, if we use the command Me.Channels(arrKeys(i)).Write sData, it would be a shame if that specific channel didn’t have the .Write method, as it will result in a script error.

This also applies for any validation layer we’d like to add to our global commands – after all, validating that the channel is an excel channel, or that it writes to a file on drive C must rely on the channel’s object to supply that information. The object must support some .Type property (to return “Excel”), and some .OutputLocaction property (to return “c:\log.txt”) in order for our validation to work.

This is where Polymorphism comes into play. Wikipedia defines polymorphism as a feature that allows values of different data types handled using a uniform interface. Simply put, it means that we can have many different types of output channels objects, but they all must support a common interface (i.e. method or property) – .Write. Once we know all our relevant objects support this interface, we can use a global command such as Me.Channels(arrKeys(i)).Write sData, knowing that no matter what exact channel is Channels(arrKeys(i)), it will have a.Write command, which takes in a single parameter.

The same goes for our extra validation layers. If we want to use a global command which operates only on output channels of a certain type, we must make the channel’s type information available through a uniform interface (e.g. the .Type property). Another option is to make the factory responsible for that piece of information (as opposed to the channel objects themselves). After all, when we created the channel, we knew its type (our .Construct method received it in the sChannelType parameter), so we could’ve recorded the type, and use it in our global validations, without bothering the channel objects themselves with providing that information.

There are many aspects to consider when deciding whose responsibility is it to provide certain pieces of information. Some code design principles call for the channel objects to be the sole source of information about themselves – this way they become more “complete” and encapsulated. Others point out that the responsibility for carrying out global actions belongs to the factory, and not the separate objects – and therefore only the factory should be responsible for that information. Theory aside, you should probably see what arrangement simplifies your scripts, and go with that.

Blind Factories

If you still remember, our original channel .Construct method used a Select-Case structure to understand which channel object it should create, according to the string parameter the user had passed to it. This seems unavoidable – we must keep a complete list of all the possible objects to create somewhere. Or must we? It would be awfully nice to rid ourselves of the maintenance headache in updating the list whenever we want to add a new type of object.

Well, it seems that even VBScript can provide was with a loophole to avoid this maintenance overhead. We can make out factory “blind” – that is, try to dynamically extract the name of the relevant object class and try to actually create it. This is done with the VBScript Execute command:

<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> Construct (sChannelName, sChannelType)
    <span style="color: #0000ff;">Dim</span> oNewChannel

    Execute <span style="color: #006080;">"Set oNewChannel = New "</span> & sChannelType
    <span style="color: #008000;">\'Continue code</span>

The execute command takes the dynamic string “Set oNewChannel = New ” & sChannelType, and executes it as native VBScript code. This means that if we send “Excel” as the sChannelType parameter, the command that will actually be executed would be:

<span style="color: #0000ff;">Set</span> oNewChannel = <span style="color: #0000ff;">New</span> Excel

In the worst-case scenario, the user will send a string that doesn’t correspond to any known object or class. Our script will simply produce an error, which can be easily dealt with via error handling techniques. This means that if we create a new type of output channel, our factory will be able to work with it immediately, out-of-the-box, with zero-maintenance. This is very similar to the CreateObject command, which can work with any COM object “out-of-the-box”, and just pop-up an error if the user attempts to create a nonexistent object type.

Notice, however, that when we turn our factory blind, we relinquish the power to store any extra-data about the objects we create within the factory itself (as it has no familiarity with the object it creates). This means that if we want to implement global commands such as .Write, and validations such as writing only to excel channels, we must implement then via very strong polymorphism in the created objects themselves. This limits the strength of our “out-of-the-box” feature – the factory works fine, as long as the requested object supports all the interfaces needed for the factory’s global commands and procedures.

An Actual Implementation

After all this tiresome theory, we can now take a closer look at real-world example. The ReporterManager Project is in fact just an extensive implementation of the Factory Pattern. Let’s examine some of its key features. These are just specific code sections from the relevant methods and classes. For the complete code, please download the ReporterManager Project, it is open-source and well documented.

First of all, a little background for those of you who are unfamiliar with the ReporterManager project. ReporterManager takes care of the reporting aspect of QTP scripting, by making it extremely easy to report events to Excel, text, windows native event log, DB and other logs. It allows you to set up as many reporters as you would like, and funnel different events to each one according to preset rules and filters.

For example, you can set up a text backup log styled for convenient reading; a special text Error log (which only includes failed steps) styled for quick skimming (+save pictures of all the errors); and a fancy Excel log, which only logs performance timers. And you can do all that with just 10 code lines.

As I’ve said, the heart of the ReporterManager mechanism is just an implementation of a Factory Pattern. It is somewhat unique in that the created objects (i.e. the different reporters) aren’t accessible directly. You can only operate them via the factory’s wrapper and methods. So if a usual Factory Pattern would handle the objects’ creation process, and let you do whatever you want from there on, the ReporterManager forces you to go through the factory for your everyday interactions with the created objects (like reporting events, shutting them down etc.).

The manager (i.e. factory) has a .StartEngine method which is used to create new reporters (i.e. output channel) and add them to the reporters’ collection. The method receives three parameters – sType (the type of the reporter), sName (the logical name by which it can be used) and sSettings (not relevant for our example). Here’s the actual creation code from that method:

<span style="color: #0000ff;">On</span> <span style="color: #0000ff;">Error</span> <span style="color: #0000ff;">Resume</span> <span style="color: #0000ff;">Next</span>
    <span style="color: #008000;">\'Try to create an instance of the reporter engine’s class</span>
    Execute <span style="color: #006080;">"Set oReporter = New "</span> & sType & <span style="color: #006080;">"Engine"</span>

    <span style="color: #0000ff;">If</span> Err.Number <> 0 <span style="color: #0000ff;">Then</span>
        bResult = <span style="color: #0000ff;">False</span>
        Reporter.ReportEvent micFail, <span style="color: #006080;">" StartEngine"</span>, <span style="color: #006080;">"Failed to "</span> &
             _ <span style="color: #006080;">"create instance : "</span> & sType
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #0000ff;">On</span> <span style="color: #0000ff;">Error</span> <span style="color: #0000ff;">Goto</span> 0

As you can see, the ReporterManager is a blind factory. The .StartEngine method receives the desired reporter type to create into the sType parameter (e.g. “Excel”), and attempts to create an object of type sType & “Engine” (e.g. “ExcelEngine”). The whole section is wrapped in an error handler, in case the desired class doesn’t exist. This means that if a new reporter type would be created, the ReporterManager code could create and use it right out-of-the-box.

After the new reporter was created, initialized and stored in the oReporter variable, our factory conducts some important validations. For example, it checks if there already exists another reporter with the same logical name. If so, the old reporter is stopped, and the new one put in its place; if not, the new reporter added to the manager’s reporter’s collection. This is a simplified version of the code, but it still demonstrates the point:

<span style="color: #0000ff;">If</span> oReporters.Exists(sName) <span style="color: #0000ff;">Then</span> <span style="color: #008000;">\'The name is unique</span>
    StopEngine(sName)
    <span style="color: #0000ff;">Set</span> oReporters(sName) = oReporter
<span style="color: #0000ff;">Else</span>
   oReporters.Add sName, oReporter
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>

The specifics of the code don’t really matter, but there is something worth noticing. On the one hand, the ReporterManager is a blind factory, which needs to execute certain global commands (e.g. StartEngine, Report etc.). In order to do so, it must rely on the reporter class polymorphism, and specifically in this code snippet, on the old reporter supporting the .StopEnigne procedure (which simply activates a .StopEngine procedure within the reporter class itself). On the other hand, the ReporterManager does know something about the objects it creates (e.g. their logical name), and is therefore able to execute at least some form of validation by itself (e.g. check whether a reporter with that name already exists).

After we’ve created the reporters, we can now use them throughout our script. This is done by calling the .Report command of the ReporterManager. This command just loops through the reporters’ collection, and activates the .Report command in each and every one of them. Again, the ReporterManager counts on the different reporter objects polymorphism, i.e. that they all indeed support such a command with the specific parameters the ReporterManager passes them. This code snippet is a simplified version of the actual code, but it still demonstrates the point:

<span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> Report(sStatus, sStepName, sExpected, sActual, sDetails)
    <span style="color: #0000ff;">Dim</span> i
    <span style="color: #0000ff;">Dim</span> arrKeys

   arrKeys = oReporters.Keys <span style="color: #008000;">\'Get all reporter names</span>

    <span style="color: #0000ff;">For</span> i = 0To oReporters.Count-1
        <span style="color: #0000ff;">Call</span> oReporters(arrKeys(i)).Report (sStatus, sStepName, sExpected, sActual, sDetails)
    <span style="color: #0000ff;">Next</span>

<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

If you want to learn more about Factories, global commands and polymorphism, I suggest you learn ReporterManager Project in depth, as it presents an extensive and elaborate use of these techniques.

Summary

We’ve seen how Factories (and Genies) can be used to further centralize our control over the objects we create and maintain in our scripts. While the Singleton Pattern provided us with the means to manage a few objects and their control their resources, the Factory Pattern allows us to multiply the concept to a dynamically growing number of objects, as well as to add an extra layer of validation to the process.

However, the implications of the Factory Pattern are much more profound. The ReporterManager implementation demonstrates how a central point of access to a storage of objects allows us to execute global commands on all/some of them. This ability can play a critical role in making our scripts simple and powerful, as one command can have a global effect on a wide array of objects.

,

Singleton Pattern

0
by on April 1, 2008 at 09:54

This article refers to design patterns, pointers and objects (discussed in the background section of this article), and classes. Make sure you’re familiar with these issues before reading on.

We begin our exploration of the world of Design Patterns with one of the simplest patterns – the Singleton. The Gang of Four eloquently summarize the intent of the Singleton pattern as follows: “Ensure a class only has one instance, and provide a global point of access to it”. We can illustrate the pattern’s concept with an example.

Background and motivation

Imagine having a script which accesses a database, or writes data to an external excel file. In order to accomplish these tasks, the script must create and utilize auxiliary objects that connects to the database or the file (e.g. a FileSystemObject, an Excel.Application or an ADO object), and perform various tasks.

If we allow our script to create these objects freely, we risk ending up with extremely inefficient code, or even worse, a script which produces errors and even breaks the system it tests. Just imagine the time wasted by creating an Excel COM object every time you report an event, or creating multiple database connection to the application’s backbone. These objects might even lock the relevant resources, thereby failing the script in unpredictable ways.

So it’s clear we should control the manner in which certain objects are created and managed throughout our scripts. This is exactly the context in which the Singleton pattern operates – it allows us to freely ask for these objects, but ensures that only one central instance of each object will be actually created and used.

Constraints and workarounds

As I’ve stated in the introduction article, Design Patterns are “meant for” object oriented programming languages. As a result, we’ll have to “downgrade” the patterns to fit into a VBScript and QTP context. As we’re about to see, this “downgrading” will be especially dominant for the Singleton pattern.

“Real” programming languages have static (AKA shared) variables and sub-procedures, and they play a critical part in any implementation of the Singleton Pattern. In case you’re unfamiliar with the terms – don’t worry. VBScript doesn’t support static elements anyway (neither does JavaScript), so we’ll have to find some workaround that will allow us to implement the Singleton pattern in VBScript without them.

I will present several implementations of the Singleton pattern, with growing complexity and usability. Some implementations may fit a certain situation better than others (not necessarily the most sophisticated ones), but they all track the same logical frame of mind.

VBScript implementations

The most basic VBScript implementation of this pattern relays on a global variable which holds the required object (it serves as a degraded form of static variable). New objects are created only if the global variable is “empty”.

<span style="color: #0000ff;">Public</span> oExcel <span style="color: #008000;">\'Announce the global variable</span>
<span style="color: #0000ff;">Dim</span> bAleadyInit <span style="color: #008000;">\'Will tell us if the object requires initialization</span>

<span style="color: #008000;">\'Check if the object is already initialized</span>
bAlreadyInit = <span style="color: #0000ff;">False</span>

<span style="color: #0000ff;">If</span> IsObject(oExcel) = <span style="color: #0000ff;">True</span> <span style="color: #0000ff;">Then</span>
    <span style="color: #008000;">\'The object was once initialized</span>
    <span style="color: #0000ff;">If</span> <span style="color: #0000ff;">Not</span> oExcel <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">Nothing</span> <span style="color: #0000ff;">Then</span>
        <span style="color: #008000;">\'The object is "alive"</span>
          bAlreadyInit = <span style="color: #0000ff;">True</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>

<span style="color: #008000;">\'Create a new instance only if needed</span>
<span style="color: #0000ff;">If</span> bAlreadyInit = <span style="color: #0000ff;">False</span> <span style="color: #0000ff;">then</span> <span style="color: #0000ff;">Set</span> oExcel = CreateObject(<span style="color: #006080;">"Excel.Application"</span>)

oExcel.Whatever \'Use the object

Now, this will work fine, but will produce tons and tons of duplicate code. So the next step will be to pack the duplicate code in a dedicated Sub-procedure:

<span style="color: #0000ff;">Sub</span> InitExcel
    <span style="color: #0000ff;">Dim</span> bAleadyInit <span style="color: #008000;">\'Will tell us if the object requires initialization</span>
    bAlreadyInit = <span style="color: #0000ff;">False</span>

    <span style="color: #0000ff;">If</span> IsObject(oExcel) = <span style="color: #0000ff;">True</span> <span style="color: #0000ff;">Then</span>
        <span style="color: #008000;">\'The object was once initialized</span>
        <span style="color: #0000ff;">If</span> <span style="color: #0000ff;">Not</span> oExcel <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">Nothing</span> <span style="color: #0000ff;">Then</span>
            <span style="color: #008000;">\'The object is "alive"</span>
             bAlreadyInit = <span style="color: #0000ff;">True</span>
        <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>

    <span style="color: #008000;">\'Create a new instance only if needed</span>
    <span style="color: #0000ff;">If</span> bAlreadyInit = <span style="color: #0000ff;">False</span> <span style="color: #0000ff;">Then</span> <span style="color: #0000ff;">Set</span> oExcel = CreateObject(<span style="color: #006080;">"Excel.Application"</span>)
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

InitExcel <span style="color: #008000;">\'Initialize the object (if needed)</span>
oExcel.Whatever \'Use the object

These implementations will work, but they are poorly designed. The code is too coupled – both the “client” (i.e. the flat code) and the procedure relay on the global variable, which means that if we ever change the global variable name or implementation we’ll be in deep trouble (it will force us to run through the code and correct all the oExcel reference). In order to solve this we could change our procedure to a function, and return a direct reference to the initialized object. This way the strong-coupling is broken, and the client (i.e. the flat code) doesn’t care where the reference originated.

<span style="color: #0000ff;">Function</span> GetExcel
    <span style="color: #0000ff;">Dim</span> bAleadyInit <span style="color: #008000;">\'Will tell us if the object requires initialization</span>
    bAlreadyInit = <span style="color: #0000ff;">False</span>

    <span style="color: #0000ff;">If</span> IsObject(oExcel) = <span style="color: #0000ff;">True</span> <span style="color: #0000ff;">Then</span>
        <span style="color: #008000;">\'The object was once initialized</span>
        <span style="color: #0000ff;">If</span> <span style="color: #0000ff;">Not</span> oExcel <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">Nothing</span> <span style="color: #0000ff;">Then</span>
            <span style="color: #008000;">\'The object is "alive"</span>
             bAlreadyInit = <span style="color: #0000ff;">True</span>
        <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>

    <span style="color: #008000;">\'Create a new instance only if needed</span>
    <span style="color: #0000ff;">If</span> bAlreadyInit = <span style="color: #0000ff;">False</span> <span style="color: #0000ff;">Then</span> <span style="color: #0000ff;">Set</span> oExcel = CreateObject(<span style="color: #006080;">"Excel.Application"</span>)

    <span style="color: #008000;">\'Return a reference to the Singleton object</span>
    <span style="color: #0000ff;">Set</span> GetExcel = oExcel
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Function</span>

<span style="color: #0000ff;">Set</span> oExcelInstace = GetExcel <span style="color: #008000;">\'Get the object</span>
oExcelInstace.Whatever \'Use the object

But wait, what about all the little oExcelInstace variables we’ll create throughout our scripts? It seems that the new design creates many duplicate instances of the excel object. However, we must remember that these are just pointer-reference to a single excel object, which is only created once. They cost us near-to-no resources, and we can easily eliminate them at will. Even if we destroy all these pointers, the real excel object will endure as it is still referenced by our global oExcel variable:

<span style="color: #0000ff;">Set</span> oExcelInstace = GetExcel <span style="color: #008000;">\'Get the object</span>
oExcelInstace.Whatever <span style="color: #008000;">\'Use the object</span>
<span style="color: #0000ff;">Set</span> oExcelInstace = <span style="color: #0000ff;">Nothing</span> \'Destroy the pointer; the real <span style="color: #0000ff;">object</span> lives on

Singleton and wrapper classes

Up until now we’ve separated our objects from the Singleton mechanism. We had an excel object, and a separated function which took care of the excel object’s lifecycle for us (or functions if we want another function in charge of destroying the Singleton object at the end of the script). While this design has its benefits, it overlooks the fact that we usually wouldn’t want to use separate functions for managing and utilizing our auxiliary objects.

Imaging we had separate functions for writing to a certain sheet of an excel file, for reading data, for querying specific values, etc. Our code will scatter and lose cohesion over time. It will become un-encapsulated, messy and hard to read and maintain. Usually, a good solution for this problem would be writing a wrapper class that encapsulates all the relevant code for an object. Here’s an example for a skeleton to a wrapper class of an excel object:

<span style="color: #0000ff;">Class</span> ExcelWrapper
    <span style="color: #0000ff;">Private</span> oExcelObject

    <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize
        <span style="color: #0000ff;">Set</span> oExcelObject = CreateObject(<span style="color: #006080;">"Excel.Application"</span>)
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Terminate
          oExcelObject.Quit
        <span style="color: #0000ff;">Set</span> oExcelObject = <span style="color: #0000ff;">Nothing</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> Open(sFileName)
        <span style="color: #008000;">\'Code for opening the file</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Function</span> GetSheetData(sSheet)
        <span style="color: #008000;">\'Code for reading the sheet and returning it as an array</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>

    <span style="color: #008000;">\'More functions that manipulate and use the object</span>
<span style="color: #0000ff;">End</span> Class

We can now modify our wrapper to implement the Singleton pattern by changing the Class_Initialize procedure:

<span style="color: #0000ff;">Class</span> Excel

    <span style="color: #008000;">\'Other class methods and variables</span>
    <span style="color: #0000ff;">Private</span> <span style="color: #0000ff;">Sub</span> Class_Initialize
        <span style="color: #0000ff;">Dim</span> bAlreadyInit

        bAlreadyInit = <span style="color: #0000ff;">False</span>
        <span style="color: #0000ff;">If</span> IsObject(oExcel) = <span style="color: #0000ff;">True</span> <span style="color: #0000ff;">Then</span>
            <span style="color: #008000;">\'The object was once initialized</span>
            <span style="color: #0000ff;">If</span> <span style="color: #0000ff;">Not</span> oExcel <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">Nothing</span> <span style="color: #0000ff;">Then</span>
                <span style="color: #008000;">\'The object has not been destroyed</span>
                bAlreadyInit = <span style="color: #0000ff;">True</span>
            <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>
        <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>

        <span style="color: #008000;">\'Only create new object if needed</span>
        <span style="color: #0000ff;">If</span> bAlreadyInit = <span style="color: #0000ff;">False</span> <span style="color: #0000ff;">Then</span> <span style="color: #0000ff;">Set</span> oExcel = CreateObject(<span style="color: #006080;">"Excel.Application"</span>)

        <span style="color: #008000;">\'Set the local class excel reference to the global Singleton object</span>
        <span style="color: #0000ff;">Set</span> oExcelObject = oExcel
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Class</span>

<span style="color: #0000ff;">Set</span> oExcelInstace = <span style="color: #0000ff;">New</span> ExcelWrapper
oExcelInstace.Open(<span style="color: #006080;">"Something"</span>)

We can create as many instances of the wrapper class as we’d like – they will all access a single excel object. In some cases the wrapper could be even more efficient – for example, if we always access the same file and sheet, the wrapper could open them in the class initialization phase. In which case, once we create a new instance of the wrapper, we know it’s immediately ready for read/write operations.

Since we want to encapsulate everything into our wrapper classes, we must implement a method that destroys the global excel object (as destroying the wrapper class only destroys the pointers to the global object). This method should be called only when the script ends, and the auxiliary object is no longer needed (i.e. not in the Class_Terminate procedure):

<span style="color: #0000ff;">Class</span> Excel
    <span style="color: #008000;">\'Other class methods and variables</span>

    <span style="color: #0000ff;">Public</span> <span style="color: #0000ff;">Sub</span> Destroy
      oExcel.Quit
      <span style="color: #0000ff;">Set</span> oExcel = <span style="color: #0000ff;">Nothing</span>
    <span style="color: #0000ff;">End</span> <span style="color: #0000ff;">Sub</span>
<span style="color: #0000ff;">End</span> Class

A QTP tweak

Shifting our focus from VBScript to QTP, we can replace the global variable with a better mechanism by using the QTP Environment object. Instead of polluting our code with unnecessary global variables, we can just use the following object creation code (either in a wrapper class or a function):

<span style="color: #0000ff;">Dim</span> bAleadyInit <span style="color: #008000;">\'Will tell us if the object requires initialization</span>
<span style="color: #0000ff;">On</span> <span style="color: #0000ff;">Error</span> <span style="color: #0000ff;">Resume</span> <span style="color: #0000ff;">Next</span>
    bAlreadyInit = IsObject(Environment(<span style="color: #006080;">"Excel_Object"</span>))
    <span style="color: #0000ff;">If</span> Err.Number <> 0 <span style="color: #0000ff;">Then</span> bAlreadyInit = <span style="color: #0000ff;">False</span> <span style="color: #008000;">\'Environment isn’t even initialized</span>
<span style="color: #0000ff;">On</span> <span style="color: #0000ff;">Error</span> <span style="color: #0000ff;">Goto</span> 0

<span style="color: #0000ff;">If</span> bAlreadyInit = <span style="color: #0000ff;">True</span> <span style="color: #0000ff;">Then</span>
    <span style="color: #0000ff;">If</span> Environment(<span style="color: #006080;">"Excel_Object"</span>) <span style="color: #0000ff;">is</span> <span style="color: #0000ff;">Nothing</span> <span style="color: #0000ff;">Then</span> bAlreadyInit = <span style="color: #0000ff;">False</span>
<span style="color: #0000ff;">End</span> <span style="color: #0000ff;">If</span>

<span style="color: #0000ff;">If</span> bAlreadyInit = <span style="color: #0000ff;">False</span> <span style="color: #0000ff;">Then</span>
   Environment(<span style="color: #006080;">"Excel_Object"</span>) = CreateObject(<span style="color: #006080;">"Excel.Application"</span>)
<span style="color: #0000ff;">End</span> If

Conclusion and prelude to the Factory Pattern

The Singleton pattern can help manage the pesky details of our objects’ lifecycles. The motivation for using it is clear – there are situations in which unmanaged creation of objects can lead to script failures and application problems.

While the Singleton pattern is considered to be a simple pattern, its VBScript implementation tends to be complicated since the language lacks static operations support. I hope that presenting the different implementations in a progressive manner helped you to understand the motives and purposes in each implementation, and to track the pattern’s relatively simple logic, regardless of its technical complexities.

The next article in the series will explore one of the more powerful Design Patterns – the Factory Pattern. We will show how we can combine it with the Singleton pattern to further centralize our control over the script’s entities and objects.

As we’ll add more patterns to our toolbox, our power and understanding will grow, and we’ll be able to present far more realistic and meaningful examples. You’ll see that the combined power of the Singleton and Factory patterns is so great, that it will enable us unravel the heart of the ReporterManager project.

,

Introduction to Design Patterns

0
by on March 29, 2008 at 09:52

This section relays heavily on your familiarity with basic code design concepts and approach. Make sure you read the introduction to code design before proceeding.

Parts of the following introduction are rewrites (more accurately downgrades) of the preface to the groundbreaking book Design Patterns: Elements of Reusable Object-Oriented Software, written by the “Gang of four”. If you’ll find the concept of Design Patterns useful, I strongly recommend you read the original book, and do it several times.

Background

Design Patterns are part of a design concept that arose from the field of architecture and city-planning. It was first specifically mentioned by Christopher Alexander the book “A Pattern Language”, where it applied for buildings, parks and the implementation of city-life principles in architectural design.

Alexander states this about Design Patterns: “Each pattern describes a problem which occurs over and over again in our environment, and then describes the core of the solution to that problem, in such a way that you can use this solution a million times over, without ever doing it the same way twice”

Even though Alexander was talking about architectural structures, the spirit of things and point of view applies to code structures just as well. We may talk of objects and functions instead of doors and parks, but in a more general sense, we too struggle with implementing the same core solutions to a repeating set of problems. In order to better understand that last sentence, let’s look at design patterns from a QTP programmer point of view.

What are design patterns?

First of all, a disclaimer. As with many other code design principles and terms, Design Patterns are traditionally “meant for” object-oriented languages. We shall make some adjustments and downgrades in order to bring the concept into the world of QTP and VBScript. So when we’ll refer to a specific pattern, or even the entire concept, we’ll probably mean something a little different than what you’ll find in the books or on the web. With that, let us continue.

So, exactly what are Design Patterns in the world of software and code? The definition is tricky, but for our purposes we’ll focus on a much narrower version of it (which would probably make the Gang of Four jump from a roof). Anyone who has ever written code finds out that he’s reusing some of the code over and over again. What’s usually done in such cases is wrapping the reusable code in a function, and calling it instead of duplicating the code.

Design Patterns extend the same basic principle. Anyone who’s written code on several systems (or, being a QTP programmer – for several systems) finds out that even if the actual code he’s writing is different than before, he’s resolving the same abstract problems over and over again. In these cases, even though the solution is Implemented in a different way each time (e.g. How do we deal with the specific error messages in the system), the solution Design is usually the same (e.g. we have a central analyzer function which receives the message and decides on the needed course of action).

So, oversimplifying, we can say that Design Patterns are these recurring problems, the context in which they exist and their conceptual solutions. Experienced programmers are able to use these solution patterns without regressing into thinking in “atomic” terms. For instance, imagine a writer who’s about to write his next novel. He doesn’t build the story one line at a time, but rather through general patterns such as “the tragic hero” or “the fallen price”.

Similarly, Experienced QTP programmers don’t plan their script by thinking of For, Next and If, one command at a time. They simply plan to write a serial search with an exit condition, as a complete and unbreakable pattern. Design Patterns make us concentrate on the big picture rather then the atomic commands, thus enabling us to easily understand big chunks of code, and bring order into chaos.

Nothing new but a point of view

As you might have understood, at some level, you’re already using design patterns. If you just shift your point of view you could see that even a simple checkpoint is just one implementation of a wide and rich design pattern (more on that later). The benefit in the new point of view can be evident by referring to “a script branching decision point” instead of a “text checkpoint”. The latter is a strict technological detail, while the former explains the meaning of the action, is opened to multiple implementations, and just makes more sense. Seeing things this way can also enable you to save tons of time by copying your solutions from project to project, even though there might not be a single common code-line between them.

At the end of the day, Design Patterns will help you program better, faster and cleaner. ’nuff said.

A modest example

An example might help us to better nail the concept. While the following structure may not count as a traditional Design Pattern, we may think of it as such, at least for our narrow QTP context. This code structure was discussed in a previous article, and today we’ll use it to illustrate the meaning of Design Patterns.

In almost any test we’re faced with the problem of script branching – i.e. deciding on how to continue the script according to a set of conditions. The most common branch involves activating a trigger (e.g. pressing a Submit button), and checking an expected result (e.g. a confirmation window opens). The next steps of the script are determined according to the actual result (e.g. pass if the window appears in under 30 sec., fail and exit the script if it doesn’t). Unlike “regular” programs, we don’t have all the data needed to make a decision in our script, and we have to go “to the real world” and check certain conditions there (e.g. does the window object exist) – usually while involving some kind of time measuring.

The most common implementation of a time-limit branching structure is the native QTP checkpoint. I’ve already discussed why this implementation isn’t quite adequate from a technical point of view, and have offered the following pattern as an alternative (for a detailed review of the discussion and this code, see the original article):

iTimer = Timer ‘<span style="color: #0000ff;">Get</span> the current time
DoTriggerEvent <span style="color: #008000;">\'E.g. : click a button, submit a form, close a window, etc.</span>
<span style="color: #0000ff;">Do</span>
    <span style="color: #008000;">\'Inner code to deal with anomalies.</span>
<span style="color: #0000ff;">Loop</span> <span style="color: #0000ff;">until</span> (Timer-iTimer>30) <span style="color: #0000ff;">Or</span> (ExitCondition=<span style="color: #0000ff;">True</span>) ‘More <span style="color: #0000ff;">exit</span> conditions <span style="color: #0000ff;">if</span> needed

<span style="color: #008000;">\'Here we wait 30 seconds before exiting the loop</span>
<span style="color: #0000ff;">If</span> Timer-iTimer>30 <span style="color: #0000ff;">Then</span>
    <span style="color: #008000;">\'Code to deal with TimeOut</span>
<span style="color: #0000ff;">Else</span>
    <span style="color: #008000;">\'More code to deal with different exit conditions</span>
<span style="color: #0000ff;">End</span> if

This code structure embodies much of the spirit and concepts of Design Patterns. It offers a very general solution, which can have a thousand different implementations according to the specific problem at hand. However, for all their differences between them, all these separate implementations track the same logical frame of mind and the same principles as the above code skeleton.

Once you’ve understood how the pattern works, you can instantly solve any problem within its domain. Of course you’ll have to sort out the specifics, but it’s safe to say the any relevant problem will be reduced to a task. This is one of the strongest benefits of fitting the world into Design Patterns – it turns out that much of the unknown world is just different implementations of the already known and scripted world.

Even switching the terminology is useful – instead of getting dragged into details and tiresome explanations, you can just tell your co-worker that you’ve implemented a time-limit branching pattern, and be done with it. If he’d ever need to, he can learn the specific details of that specific implementation, but even without the details, the code will immediately become readable, and even skimmable. And you know what? After a while you won’t even need to tell your co-worker – the pattern has such a strong signature (once you’re familiar with it), it immediately stands out as a complete logical unit, regardless of the exact implementation.

Where do we go from here?

This article was simply meant to present the general concept of Design Patterns and explain it in the least technical sense possible. Since it’s such a tricky and elusive subject, I’ve used lots of examples and analogies, and I hope that they’ve served their purpose. In future articles we’ll approach Design Patterns from a more technical point of view, and see how they can fit into our QTP scripts and techniques.

It’s quite alright if the concept still eludes you. Quoting the Gang of Four themselves, in the preface to their book: “Don’t worry if you don’t understand this book completely on the first reading. We didn’t understand it all on the first writing!”. Hopefully, once we’ll introduce some more patterns and implementations, both the concept and the details of Design Patterns will be made clear.

While you wait for future articles in this section, I strongly recommend you skim through some of the background Design Patterns material on the web. Here’re some recommended resources:

Design Patterns @ Wikipedia.

Design Patterns @ go4Expert.

Old yet “Really good books” on Design Patterns.