To Plug-in or Not to Plug-in
I often hear the question of what is the best way to extend TestComplete or to add common routine libraries to test projects. In this article we will examine the various options for adding functionality to TestComplete as well as the pros and cons for each technique. But first, let's take a look at the reasons why you might want to extend TestComplete.
Probably the most frequent reason users may wish to extend Testcomeplete is that they want to make available common tasks or libraries of common routines to multiple test developers and test projects. Perhaps you have some logic that is going to function the same way in every test project and you wish to avoid rewriting the code.
Another reason might be that you wish to add functionality to TestComplete that is not normally found in the product: integration with other tools, access to your own tested application's data, etc.
A third reason could be for the purpose of providing abstraction layers between your test script code and the objects in your tested application. I can provide an example of this from my own current project: The application under test is a complete .NET rewrite of an existing product. Thus it is new development and is in a state of constant flux, the prototype screens undergoing change, and control libraries being chosen. As a result, I'm not entirely sure what grid controls will be used in the final product. Nevertheless, I need to be prototyping tests and developing some test suite shells for future builds. Rather than writing test scripts directly against the grid control that may change later, I'm writing a grid abstraction layer. I can code to my abstracted grid, and the abstraction will talk to whatever grid ends up being chosen.
You have a few options when it comes to how you will implement extensions to TestComplete. The ones that we'll examine are shared script units, ODT classes and plug-ins.
Shared script units are pretty much exactly what they sound like: TestComplete source code units that are shared amongst multiple test projects. Venerable common libraries of routines just like the ones used for years by developers. The idea here is this: imagine you have a number of test projects, and you write a script routine called OpenDataFile which is able to open your tested application's particular data source. You might wish to use this routine in every one of your test projects, but really don't want to copy the function to each of them. Besides the tedium of hand copying all that code from one project into the next, there's also the issue of maintainability. If there are multiple copies of the function in question, that's multiple opportunities for the code in each instance to get out of synch with the others.
The preferable solution here is to share one instance of the source code file containing the common routines with each project that needs it.
A more robust option is that of ODT (Object-Driven Testing) classes. There is a considerable amount of information in the TestComplete online help on the subject of ODT and its various facets, both as a tool for definition of test classes and as a sort of visual environment for implementation of data-driven testing. The ODT option to which I refer is the former: as a tool for the declaration of and implementation of testing classes. If you're not familiar with ODT, or if you're familiar but have not used ODT in this way, allow me to briefly explain. TestComplete, by way of the ODT plug-in, allows for a sort of object-oriented approach to the creation of reusable test code. You can create class declarations, populated by properties and methods. These test classes can then be created, as needed, as test objects. Whether they are used as object wrappers (like my grid wrapper I mentioned), as part of a data-driven testing paradigm, or as containers for utility code, the ODT classes are an excellent option for extending TestComplete.
The third option we'll discuss here is that of custom plug-ins. The TestComplete architecture is such that all features of the product are implemented through plug-ins. The standard functionality of the tool, things you do with TestComplete on a daily basis are all added into the product via plug-ins. This plug-in architecture is available to you, the end user, so as a result, it's possible to build plug-ins for TestComplete that feel just as standard and as native as any other part of the product. The obvious benefit here, of course, is that your custom plug-in, perhaps containing elements specific to your test environment and your development practices, can be embedded in TestComplete right next to the standard UI panels.
Figure 1 - my own custom reporting panel, smack between the Test Log and Test Suites panels.
Figure 2 - another of my custom plug-ins (CodeGenerator) residing among Local Variables and Watch List.
Likewise, functions made available from your plug-in can be seen in the code-completion window.
Figure 3 - my custom plug-in's functions in Code Completion.
Plug-ins in TestComplete offer an entirely new option concerning plug-in integration and sophistication: not only can you plug-in additional scriptable functionality, visual panels and engine options, but you're also able to create extensions to version 4's Project Explorer and Workspace via custom project items.
Figure 4 - your own plug-ins can grace the Project Workspace of TestComplete 4 with not just panels, but also custom project items.
This level of sophistication might not make much sense until you've used TestComplete 4 or possibly until some custom plug-ins are created using this capability, but it offers some serious power and flexibility on the extensions front.
Pros and cons
As I mentioned, there are advantages and disadvantages to each of these methods of extension, and your own needs and strategies will affect the balance even more. Here I will enumerate the major benefits and detriments to each approach. Where possible, I'll also try to share from specific experience. Please note, where I am listing "cons" be aware that these are not shortcomings of TestComplete itself, rather with the specific option for extending TestComplete.
Scripts Pro: Simplest Implementation. Scripts are by far the easiest of the extension options to implement. If you wish to create a library of common routines to share among multiple projects, it's as simple as creating the unit, writing some scripts, saving the unit in a commonly accessible location, and then adding the unit to any projects you wish to share in the functionality.
Scripts Pro: Familiar to scripters. Common script libraries do not require expertise beyond the ability to write script code. Thus, those who would use the libraries can write them.
Scripts Pro: Easiest to maintain. Common script libraries are not compiled, nor do they require any extra effort to get to. When you wish to update or refactor your routines, you can simply get at the shared unit and have at it. However, as we'll soon see, that's not always a good thing.
Scripts Con: must add to every project. To share your common routine libraries amongst projects, you'll need to add the units to every project you wish to access them. This may not seem like that big a problem, but in my current situation, I'm managing dozens of test projects, with many more to come, and if I wanted to take this approach, I'd have to manually go through the process of clicking Add Unit, browsing to the unit, etc. for every project. Tiresome.
Scripts Con: No standardization or security. A unit full of script routines is rather freeform, dangerously so. There is little or no contract or requirement in the interfaces of the routines. Unless you are able to maintain utterly rigid coding standards, the various routines will bear the differing marks of whichever tester wrote them. Along those same lines, there is no protection from accidental modification. The shared units can be accessed and altered by anyone and since those routines are used by multiple projects, the side effects of change can be far reaching and devastating.
ODT (Object-Driven Testing)
ODT Pro: OOP emulation. One of the most noticeable advantages of ODT classes is that they provide a sort of object-oriented programming for script-based (i.e., procedural language) test projects. That statement, of course, requires some qualification. The TestComplete scripting languages are more than procedural and ODT is only an emulation of OOP techniques - not true object-orientation, but let's not pick nits. With ODT classes, you can, if you wish, create implementations of classes that simulate inheritance and polymorphism. This is done primarily through ODT's ability to remap method implementations to instantiated objects. I'm not going to try to explain that here; you can see the TestComplete help section on ODT for more.
ODT Pro: reusable code. As with common script libraries, ODT classes give you a write-once/reuse-many advantage. You can have, for instance, your button-wrapping class, which you write and implement once, and then create objects of that class throughout your projects, reusing the code.
ODT Pro: self-referential code. This is a little obscure, but noteworthy. ODT classes feature another advantage of object-oriented programming: the ability for objects to easily reference themselves using the This keyword (or Self in DelphiScript). This enables you to, for instance, write an ODT class that represents a customer, with properties appropriate to a customer, like name, address, etc. A customer object's methods, using This (or Self), can then access its own properties, perhaps getting the data from those properties and then inserting that data into a data entry form.
ODT Pro: visual or programmatic development. One of the nice things about the ODT plug-in is that it's possible to create your class declarations either in the visual ODT panel, or programmatically, using the ODT object. The programmatic approach is advantageous for managing your test code in a source control system. See below for a discussion of ODT's INI-based nature.
ODT Con: Need to add code to every project. Just like with common scripting libraries, to use an ODT class in multiple projects, you'll need to add the units which contain the implementing code to every project where you use the class. In addition, if you're declaring the class visually, you'll have to add the class declaration to every project as well.
ODT Con: It's still script-based. Even though ODT presents some advantages over straight script code, it's really still just script code. It shares all the disadvantages as the shared routine units.
ODT Con: ODT persistence is INI-based (not 2-way). One of the cool things about the Delphi and Visual Studio .NET form designers is that they persist their designs in code that can be edited. If you edit the code, the designers update to reflect those changes. If you make a change in the designer, the code is updated. This is called a two-way editor. The disadvantage of ODT persistence is that classes that are designed in the ODT panel are stored in a proprietary INI file format. As some have seen, this prevents multiple test developers from editing a single project's ODT classes and data at the same time, if the project is under source control.
ODT Con: Performance in large projects. When the visual panel is used to implement a large number of objects as well as the class declarations, test playback performance can be affected. Personally, I do not use the data section of the ODT panel to instantiate objects. I define my classes in the panel, but then create the objects in code using ODT.Classes.New().
Plug-in Pro: Deployment. This is a big plus. This is the primary reason why I'm now making a prodigious use of compiled plug-ins. Rather than having to install into every project to make use of it, plug-ins are installed once, and available to every project, just as any other part of TestComplete is. Thus, you never have the overhead of adding units to projects.
Plug-in Pro: Ease of use. If you're familiar with TestComplete's Code-Completion window, you know that installed plug-ins display their available properties and methods in the window, from which test developers can select items to enter into their test scripts. Also, some plug-ins provide a set of options that can be edited in the Options|Engines dialog. Your own installed plug-ins can do the same; making their properties and methods available to Code-Completion and providing their options to the Engine Options dialog.
Plug-in Con: Maintenance. Plug-ins are not as accessible to maintenance as the script-based solutions. To maintain (update, edit, refactor, etc.) a compiled plug-in, you must do so in the native language in which the plug-in was built; not in TestComplete.
Plug-in Con: SDK. The plug-in development SDK for TestComplete 3 plug-ins is very large and undocumented. While there is a decent simple plug-in tutorial in the help file, there is no object reference and no comments in the SDK source at all. However, I've been able to ferret out a good bit of the TC3 SDK - just in time for TC4
Be aware, the TestComplete 4 SDK is necessarily even more complex. But I have to say it: there sure is a lot you can do with these SDKs.
Plug-in Con: Delphi or C++. I am a long-time Delphi developer and I love it. However, I'm at a company now that does not, and I'm dealing with the fact that I must use a non-approved language (Delphi) to develop my TC3 plug-ins.
I hate to end this on negative points, so let me say this: there is no bad option for extending TestComplete. Depending on your needs, all these options I've presented are powerful, flexible possibilities and I currently have instances of all three methods in use in my test projects, but as time goes on, I have more and more custom plug-ins installed