|Ernesto Guisado's Website » Articles » Testing Win32 GUIs with Perl and C||Articles | Miscellanea ||
Testing methods vary widely depending on the code being tested. Programs designed to interact with other programs can be easily tested. If your end-product is a C++ library, for example, you can easily generate a test program that makes use of its functions. For GUI-based programs, Dynamic Testing is a common solution, where you simply fire up the program and use it in the way a typical user would. If your organization has an effective software development process, you might have a test case document detailing all the things that you should be trying out and the expected outcome. For a program of any complexity, this means a lot of time. Testing GUI programs is a challenging task. So challenging in fact that the first rule of GUI testing is: Don’t!
I don’t mean you shouldn’t test at all, but you should design your programs in a way that allows them to be tested without relying on costly GUI testing. If you use a sound architecture separating your program into a GUI part and an engine or server part that does all the dirty work, you can test all the functionality of the engine without actually using the GUI. What remains to be tested is actually checking that the buttons are "hooked" to the right functions (of course, I’m simplifying grossly here).
Even if you’ve tried hard to separate user interface from
engine code, you may still want to test your GUI. I’ve
Win32::GuiTest Perl module (see Resources)
in a combination of Perl and C. It bundles together the facilities
for GUI testing offered by the Win32 API with some powerful
general-purpose Perl functionality. The combination helps automate
the GUI testing process.
The first step is starting the application that you want to test. Perl offers the system function:
The system function has one drawback: it doesn’t return until the application you’ve invoked is closed. The windows command shell (cmd.exe) supports the start command allowing you to start an application in a new window, thereby avoiding this limitation. The start command also allows a certain degree of control over the application you’re starting. The following code starts an application but in a maximized window:
system("start /max notepad.exe");
If you’re invoking a console application, you might want
to process the text output somehow. In that case, Perl’s
qx// operator is more useful. If you need some extra
control you can also use the
module. This allows suspending, killing, or waiting until the
Once you’ve started the application, you want to make sure
that you’re "talking" with the right window. You don’t
want to send the keystroke commands to your e-mail program
inadvertently and end up sending some random API calls to your mom.
The usual approach is to traverse the window hierarchy looking for
a window with the right name or attributes. To that effect, the
Win32 API provides the
I’ve created a Perl wrapper called
FindWindowLike around this function.
FindWindowLike returns all the children of a given
window that match a particular title, window class, or control id.
FindWindowLike also allows limiting the search to a
particular depth and makes full use of Perl’s powerful
regular expression engine.
my @w = FindWindowLike(0, "^Microsoft Excel", "^XLMAIN\$");
This example looks for Microsoft Excel using pattern matching on
the window title and the window class. The first argument indicates
that the search should start with the desktop window. If
you’re looking for a button, you might prefer starting with
the dialog box window instead. Once you’ve found the window,
you might need to bring it to the foreground to be able to interact
with it. The
SetForegroundWindow function is just a
wrapper around the Win32 function of same name. On Windows 2000,
using this function might be problematic because only the process
that created the window is allowed to bring it to the foreground. A
workaround is to use the
ALT+TAB key sequence to
change the foreground window.
Now we’re ready to tell the window what to do. The
SendKeys function is a clone of the function with same
name that you have in Visual Basic and is built upon the
keybd Win32 API function.
SendKeys uses a
string to represent the characters that you want to send to the
active window. The window effectively behaves as if the end user
had typed those characters into the keyboard.
also allows certain meta-characters in the string specifying things
BACKSPACE. Consult the
documentation for a full description of the supported format. The
following example sends the key sequence
For completeness sake, GuiTest also includes functions that allow simulating user input using a mouse. The following code opens a well-known paint program and draws a triangle in it:
system("start /max pbrush.exe"); sleep 2; MouseMoveAbsPix(100,100); SendLButtonDown(); MouseMoveAbsPix(500,500); MouseMoveAbsPix(100,500); MouseMoveAbsPix(100,100); SendLButtonUp();
After you’ve made your application jump through some hoops
SendKeys and the mouse functions, you actually
have to verify that the application worked as expected. As
we’ll see in the next section, a whole range of errors
(namely navigation errors) are detected simply by running the
script: if the script is able to finish, the navigation has to be
In order to test your application more thoroughly, you’ll have to apply some ingenuity. No silver bullets for this part. However, I’ll give you some ideas on how to do it using the functions in GuiTest.
See the fonts.pl script adapted from
the GuiTest distribution for an example on how to extract
information from a dialog box using a combination of
This script needs to be adapted to the language of the machine you
plan to use it on. I’ve marked those dependencies with the
Using the mouse handling functions to navigate through the menus and dialog boxes in an application is difficult. You have to rely on knowing the exact layout of the controls, because you have to specify exact coordinates for each mouse movement. It is much easier to navigate using the keyboard. These are the main tools to help you in that area:
CTRL+Cfor copy is a good example).
ALT+fto open the File menu.
TABand correct tab order to help you navigate through window controls.
SPACEto accept a dialog box.
If you get your tabs wrong or forget some hotkeys, the user might still be able to make his way using the mouse, but your test script will break. GUI testing your application actually forces you to follow certain minimum usability standards when building the application.
The example script (download here) also serves to illustrate some
problems related to localization (l10n). The fonts.pl script uses
the caption of the font dialog box to verify that the menu
navigation worked. "Font" in Spanish is Fuente, so this
needs to be changed. The script also uses the
key sequence to navigate to the font dialog box. This depends on
the particular menu names and hotkeys assigned in your language
version of Notepad. Some GUI specific localization dependencies you
need to be aware of are as follows:
CTRL-N(for New) might be
CTRL+Uin Spanish. Some shortcut keys are widely used (like
Vfor copy/cut/paste), but in most cases shortcut keys are considered mnemonics and therefore different from one language to the next.
In the end, this means quite simply that you’ll have to adapt your test script for the different languages that you plan to support. In that case, it will pay-off nicely if you’ve used sound programming techniques even in your test scripts. If you’ve done that, you’ll have just one file shared by all scripts that defines the key sequences necessary to reach any GUI element. This is the second law of GUI testing: Test-script development is software development. Don’t treat it as a second-class activity. All the nice things that you learned to help make programs more maintainable apply to your test scripts as well. I strongly believe this to be true and as a consequence, I don’t plan to implement any type of keystroke recording program because I believe that this encourages sloppy programming and treating test scripts as a throw-away side product.
Being written in Perl, GuiTest plays well with other Perl
modules. Some popular ones are
pretty common scenario is some GuiTest user finding a creative use
of the module by combining it with
which I normally try to put the corresponding Win32 API function
into GuiTest, to make it more convenient to use. This is what
happened with the PostMessage function.
Not surprisingly, a lot of people use GuiTest for GUI testing
(stress testing or monkey testing being particularly popular), but
others use it for a wide range of ad-hoc tasks, from transferring
mail between incompatible systems to automating a GUI program to
perform a tedious nightly task. A common Win32 technique that has
proved useful in a wide variety of contexts is using the
SendKeys function to send the
combination. This allows switching between open windows just as if
a user had done it:
GuiTest has its limitations. As I mentioned, it lacks a keystroke recording utility. Another often requested feature is being able to take screen-shots and comparing them with saved images. This would be useful for regression testing because you could take a screen-shot of a dialog box and later on detect any changes to it.
Part of my background is in software localization, so I’m a bit wary of this functionality. Localizing the GUI changes the layout a lot — different languages have different lengths for strings (Spanish and German strings are at least 20 percent longer than English strings), and this means changing the sizes of dialogs and controls. A screen-shot-based test suite is useless to test the localized version of your product. Also, maintaining this kind of test suite is a pain during development because dialog box layout changes a lot during prototyping and usability testing. Even so, if demand is high for this functionality, I might eventually include it; in fact, some users have already sent me patches to implement parts of it. If you think this would be a worthwhile feature, drop me a line and let me know.
Win32::GuiTestPerl module is updated regularly. The latest version is available at the following URLs:
I’d like to thank all the people who have helped me with GuiTest over the last few years. You’ll find some of them mentioned in the README file that comes with the distribution. There is a Yahoo! Group dedicated to the use of GuiTest. You can join it at: http://groups.yahoo.com/group/perlguitest/join.