Let's start

Although it is not always true we can say that generally, all windows which are opened on your screen are system objects. Win32 keeps track of them and can give information about them. Let's practice from the very beginning. Launch the calculator application available in Win32 systems. On my machine it looks like this:

If it does not look like this on your machine, go to "View" menu in Calculator and choose "standard". Although keeping "scientific" will not do any harm, using the standard version will help us avoid confusion.

Now run WinSpy.

Click the "Full screen" button as shown above...

and then "More" to get the window given below:

On the right side, you can see a tree, reminding a tree of catalogs. Find an item, which is named exactly in the same way, as your Calculator window. The title of the window, also called caption is placed here. In my case it is "Calculator". If you use Win32 with different language, the title may differ. Whatever the title, try to find an item with this title in WinSpy tree.

Got it? Now place Calculator and WinSpy in a way that they don't overlap so you can see them both. Click on the item you've just found in WinSpy and click "Flash" button in the bottom right corner of WinSpy window. Did you notice that? Borders of Calculator were blinking for a moment. This feature helps you to make sure that the item from the tree relates to the right window. Try to experiment with other items. Go to the top of the tree. Choose "Desktop window" item. Click "Flash". Frame of the the whole screen blinks. Try with other items as well.

Once you played a little bit, we can come back to Calculator.

Imagine that you want to automate the following scenario:

It requires that you press the following keys: 2, +, 5 and =.

If a user want to do such an operation on Calculator, he/she has to do the following:

Actually, in the step one, clicking on the opened application (or using Alt-Tab to switch to this application) is a way of doing something important. You move the "input focus" to Calculator. In Win32, window needs to have a focus in order to receive all input from the keyboard. When you have several applications opened, and you type something, only one application, this with the input focus, actually react on what you type.

OK. We have prepared the first test case scenario. Let's write a perl script, which will do it for us. Let's assume that Calculator application is opened when the perl script starts.

Firstly the script has to move the input focus to Calculator. But which window is Calculator? And how to manipulate this window?

We will need to use the function FindWindowLike:

    use Win32::GuiTest qw( FindWindowLike );
    use strict;
    my @whnds = FindWindowLike( undef, "^Calculator" );
    if( !@whnds ){
        die "Cannot find window with title/caption Calculator\n";
    }else{
        printf( "Window handle of calculator application is %x\n", $whnds[ 0 ] );
    }

Of course, if you are using Win32 for different language (like Polish), You will have to change the search title to the right one ("Kalkulator" for Polish).

FindWindowLike returns a list of window handles. Window handle uniquely identifies a window in the system. In any time, there could be only one window with the given window handle, which is a long number.

Why does it return a list? We want only one window, don't we? FindWindowLike is a flexible function, which allows to specify search conditions for the group of windows. In that case, it will return handles of all windows, which satisfy these conditions. In our call, we specify that we are looking for a window with title "Calculator". In most cases only Calculator window will match. However, if there is another window opened which contains "Calculator" string in its title, its handle will also be returned. The second parameter of FindWindowLike function is treated as a regular expression to match with all windows' titles.

Try to run it now. If your calculator application is opened you should get something like:

Window handle of calculator application is 103B6 (hex). Note that it matches the window handle shown by WinSpy on the picture above. Remember - WinSpy shows most of numbers in hexadecimal notation. For perl, the default notations is decimal. Keep that in mind while looking for reasons, why perl script does not display the handle displayed by WinSpy.

So we have found the window. Now we have to move focus to the window and then type the right keys.

We move focus using function SetForegroundWindow. And we send keys using SendKeys.

Our script will look like this:

    use Win32::GuiTest qw( FindWindowLike
                           SetForegroundWindow
                           SendKeys );
    use strict;
    my @whnds = FindWindowLike( undef, "^Calculator" );
    if( !@whnds ){
        die "Cannot find window with title/caption Calculator\n";
    }else{
        printf( "Window handle of calculator application is %x\n", $whnds[ 0 ] );
    }

    SetForegroundWindow( $whnds[ 0 ] );
    SendKeys( "2{+}5=" );

SetForegroundWindow takes a handle to a window, which should receive the input focus. The SendKeys sends the keys to the window, which has the input focus. "+" has a special meaning for SendKeys so it has to be taken in braces.

Let's try. When you run the script, the Calculator's window will move to front and then it will display "7" (before it displays "7", it will also display "2" and "5", however it would be done so quickly, that you will not notice that).

That was easy. Let's go a step further. You can input data into Calculator in 2 ways - by keyboard or by clicking the right buttons on calculator's panel. It's time to test the second way of using Calculator.

Have a look at WinSpy again. Notice that Calculator item in WinSpy tree has a plus. Plus suggests that you can expand an item and find something more in it. Let's do it.

You can see more items. What are they? Well, check it yourself. Try to highlight them and see what blinks. You will quickly realize that these items are Calculator's buttons and display. They are windows too! Windows, which have some characteristics different from Calculator's main window, but they are windows.

Find the window, which is a "2" button. Click it. Look what WinSpy says about it. It gives you the window handle. It also display information about control id.

Each window has a control id. It does not have any particular meaning for main windows, however, it does for windows, which belong to other window (are children of other window). Such windows (belonging to other window) are also called controls. Control id is used to uniquely identify child window among other children on the same parent window (this is not always true. It may happen that a parent have several children with the same id). Find all buttons we need and write down their control ids. They should be:

ButtonControl id (Hex)Control id (Decimal)
"2"7E126
"5"81129
"+"5C92
"="70112

Notice, that control ids are static. This means, they are always the same for each instance of the same application. Control id for "2" button is 7E on my machine and is also 7E on your machine. Control ids are given to controls when the program is designed. They are hard coded in the program. Window handles are not. If they are the same on my pictures and your machine it's a pure accident. Window handles are dynamic. A window is given a handle every time it is created by the system. If 2 instances of the same application are created (say, you will run Calculator twice), they will have totally different set of windows' handles.

Our "buttoned" test will look like this:

  • Find a window

  • Push all necessary buttons.

You push a button by calling PushChildButton. It takes 2 arguments : window handle of the window on which the button is placed (parent window), and the control id of the button you want to push.

Here is the code:

    use Win32::GuiTest qw( FindWindowLike
                           PushChildButton );
    use strict;
    
    my @whnds = FindWindowLike( undef, "^Calculator" );
    if( !@whnds ){
        die "Cannot find window with title/caption Calculator\n";
    }else{
        printf( "Window handle of calculator application is %x\n", $whnds[ 0 ] );
    }
    PushChildButton( $whnds[ 0 ], 126 ); # Button 2
    PushChildButton( $whnds[ 0 ], 92 );  # Button +
    PushChildButton( $whnds[ 0 ], 129 ); # Button 5
    PushChildButton( $whnds[ 0 ], 112 ); # Button =

Notice that you do not have to move the input focus to Calculator. That's because you specify exactly, which button in which window you want to push.

Run it. Again, Calculator should display 7.

We are now able, to make the script doing typing and pushing for us. The really automated test also requires that the results are automatically checked. So the thing we miss is checking if when adding 2 and 5 Calculator actually displays 7.

We have to go back to WinSpy again. On the very beginning of controls belonging to Calculator, there should be a control of type "Edit". As you may guess, Edit control is also a window, with some special features, like displaying a text. You get the contents of the Edit control by calling WMGetText. It takes as a parameter a window handle of the Edit control. So in order to use it in our code, we have to find out a window handle for the Edit control. We actually have to implement finding this handle. We cannot hard code it, since you never know what would be the window handle of any window. But when you have the control id and window handle of a window, which is a parent of the Edit control, you can easily get window handle of that control. The function, which is going to help you is again FindWindowLike.

First the script, then the explanation:

    
    use Win32::GuiTest qw( FindWindowLike
                           PushChildButton
                           WMGetText );
    use strict;

    my @whnds = FindWindowLike( undef, "^Calculator" );
    if( !@whnds ){
        die "Cannot find window with title/caption Calculator\n";
    }else{
        printf( "Window handle of calculator application is %x\n", $whnds[ 0 ] );
    }
    PushChildButton( $whnds[ 0 ], 126 ); # Button 2
    PushChildButton( $whnds[ 0 ], 92 );  # Button +
    PushChildButton( $whnds[ 0 ], 129 ); # Button 5
    PushChildButton( $whnds[ 0 ], 112 ); # Button =

    my $edit_ctrl_id = 403; #Edit window, 193 Hex
    my @edit = FindWindowLike( $whnds[ 0 ], undef, "^Edit", $edit_ctrl_id );
    if( !@edit ){
        die "Cannot find window handle for Edit control\n";
    }else{
        printf( "Edit window handle is %x\n", $edit[ 0 ] );
    }
    my $result = WMGetText( $edit[ 0 ] );
    if( $result != 7 ){
        print "Test failed. The result is $result and expected value was 7\n";
    }else{
        print "Success. The result is $result, which is as expected\n";
    }

Note, that in the second call of FindWindowLike you don't have to give "^Edit" regular expression as a class name. Parent window and control id, in that particular situation, is enough. But we will use this redundancy as a fuse.

As you can see, the only thing we add is finding window handle of edit window and then get text from this window. Then the text is compared with the expected value.

You should get something like this:

Note: this time main window handle is different. This is because I closed Calculator and ran it again. As you already know, it was given a different window handle during the second run.

Congratulations! You've done the fully automated GUI test.