Squish records tests using the scripting language that was specified for the test suite, rather than using a proprietary language. Once a test has been recorded we can run the test and Squish will faithfully repeat all the actions that we performed when recording the test, but without all the pauses that humans are prone to but which computers don't need. It is also possible—and very common—to edit recorded tests, or to copy parts of recorded tests into manually created tests, as we will see later on in the tutorial.
Recordings are always made into existing tests, so we must begin by
creating a new "empty" test. There are two ways we can do this. One way
is to click |. This will pop up the New Squish Test Case wizard (Section 16.3.6)—simply enter the name
for the test case and then click . Another
way is to click the New Test Case toolbar button (to the right of the
"Test Cases" label in the Test Suites view); this will create a new test
case with a default name (which you can easily change). Use one of these
methods and give the new test case the name “tst_general”.
Squish automatically creates a sub-folder inside the test suite's
folder with this name and also a test file, in this case
test.py. (If we had chosen JavaScript as our
scripting language the file would be called
test.js, and correspondingly for Perl or Tcl.)

To make the test script file (e.g., test.py) appear
in an Editor view (Section 16.2.6), click—or double-click
depending on the
||| setting—the test case.
(Incidentally, the checkboxes are used to control which test cases are
run when the Run Test Suite toolbar button is clicked; we can always run
a single test case by clicking its Run Test button.) Initially, the
script is empty. If we were to create a test manually (as we will do
later on in the tutorial), we must create a
main function. The name "main" is special to
Squish—tests may contain as many functions and other code as we
like (providing it is legal for the scripting language), but when the
test is executed (i.e., run), Squish always executes the
main function. This is actually very convenient since it
means we are free to create other functions, import libraries, and so
on, without problems. It is also possible to share commonly used code
between test scripts—this is covered in the User Guide (Chapter 13). (In fact, two other function names are special
to Squish, cleanup and init; see Tester-Created Special Functions (Section 14.1) for details.)
Once the new empty test case has been created we are now free to write test code manually, or to record a test. If we choose to record we can either replace all the test's code with the recorded code, or insert recorded code into the middle of some existing test code. We will only concern ourselves with recording and replacing in the tutorial.
![]() | For command-line users |
|---|---|
Creating a new test case from the command line is an easy two-step process: first, create a test case directory; and second, create an empty test case script.
|
Before we dive into recording let's briefly review our very simple (and far from thorough) test plan:
Open the MyAddresses.adr address file.
Navigate to the second address and then add a new name and address.
Navigate to the fourth address (that was the third address) and change the surname field.
Navigate to the first address and remove it.
Verify that the first address is now the new one that was added.
We are now ready to record our first test. Click the toolbar button (
) that's to the right of the
tst_general test case shown in the Test Suites view (Section 16.2.16)'s Test Cases list. This will cause
Squish to run the AUT so that we can interact with it. Once the AUT is
running perform the following actions—and don't worry about how
long it takes since Squish doesn't record idle time:
Click
|,
and once the file dialog appears, type
MyAddresses.adr into the line edit, then click the
button.
Click the second row, then click |, then click the first line edit in the Add dialog and type in "Jane". Now click (or tab to) the second line edit and type in "Doe". Continue similarly, to set an email address of "jane.doe@nowhere.com" and a phone number of "555 123 4567". Don't worry about typing mistakes—just backspace delete as normal and fix them. Finally, click the button. There should now be a new second address with the details you typed in.
Click the fourth row's second (surname) column, delete its text and replace it with "Doe". Then press Enter to confirm your edit.
Now click the first row, then click |, and then click the button in the message box. The first row should be gone, so your "Jane Doe" entry should now be the first one.
Click the toolbar button in the Squish Control Bar.
This will make the Squish IDE appear. In the Application
Objects view expand the Shell object, and then the
Table object. Now expand the row_0 object.
Click the column_0 object to make its properties appear in
the Properties view (Section 16.2.11), and then check the
text property's checkbox. Now click the
column_1 object and check its text property.
Finally, click the button (at the
top-right of the Verification Point Creator view (Section 16.2.19)) to
have the forename and surname verifications for the first row inserted
into the recorded test script. (See the screenshot below.) Once the
verification points are inserted the Squish IDE's window will be hidden again
and the Control Bar window and the AUT will be back in view.
We've now completed the test plan, so in the AUT click |, then click in the message box, since we don't want to save any changes.

Once we quit the AUT, the recorded test will appear in Squish's IDE as the screenshot illustrates. (Note that the exact code that is recorded will vary depending on how you interact. For example, you might invoke menu options by clicking them or by using key sequences—it doesn't matter which you use, but since they are different, Squish will record them differently.)

If the recorded test doesn't appear, click (or double-click depending on
your platform and settings) the tst_general test
case; this will make Squish show the test's
test.py file in an editor window as shown in the
screenshot.
The SWT-specific chooseFile function
returns a filename as if the user had interacted with a standard
windowing-system supplied file open dialog. Squish records the
absolute path of the file to ensure reliability when running tests, but
for this tutorial we have used scripting language facilities to provide
a filename that will work wherever the example is installed.
Now that we've recorded the test we are able to play it back, i.e., run it. This in itself is useful in that if the play back failed it might mean that the application has been broken. Furthermore, the two verifications we put in will be checked on play back as the screenshot shows.
Inserting verification points during test recording is very convenient. Here we inserted two in one go, but we can insert as many as we like as often as we like during the test recording process. However, sometimes we might forget to insert a verification, or later on we might want to insert a new verification. We can easily insert additional verifications into a recorded test script as we will see in the next section, Inserting Additional Verification Points (Section 4.4).
Before going further we will look at how to record a test from the command line. Then we will see how to run a test, and we will also look at some of the code that Squish generated to record the test and discuss some of its features.
![]() | For command-line users |
|---|---|
First and foremost, the squishserver must always be running when recording or running a test. This is handled automatically by the Squish IDE, but for command line users the squishserver must be started manually. (See squishserver (Section 15.4.2) for further details.) To record a test from the command line we execute the squishrunner program and specify the test suite we want to record inside and the name we want to give to the test case. For example (assuming we are in the directory that contains the test suite's directory): squishrunner --testsuite suite_py --record tst_general --useWaitFor
It is always best to record using the |
To run a test case in the IDE just click the toolbar button (the green right-pointing triangle that appears when the test case is selected in the Test Suites view (Section 16.2.16)). This will cause Squish to run the AUT and replay every action (omitting human idle time, but allowing just enough time for the GUI toolkit to keep up). It is worth trying out since it has quite an impressive effect, especially if you haven't seen it in action before.
When we have two or more test cases we can run them individually by clicking the test case we want to run to select it and then clicking the button, or we can run them all (one after the other) by clicking the toolbar button (which is above and slightly to the left of the button. (Actually, only those test cases that are checked are run by clicking the toolbar button, so we can easily run a particular group of tests.)
![]() | For command-line users |
|---|---|
As noted earlier, the squishserver must always be running when recording or running a test. (See squishserver (Section 15.4.2) for further details.) To play back a recorded test from the command line we execute the squishrunner program and specify the test suite our recorded script is in and the test case we want to play. For example (assuming we are in the directory that contains the test suite's directory): squishrunner --testsuite suite_py --testcase tst_general |
In some cases it is possible that running the test will fail with an error message similar to this:
ERROR Mon Mar 23 15:41:12 2009 test.py:31: Script Error
Error in closeWindow() invocation: null object
This sometimes happens if a dialog is dispose()d too quickly.
Simply go to the offending line and comment out the
closeWindow() call (which is on line 18 in this case) and
re-run the test. The test should now work fine.
If you look at the code in the screenshot (or the code snippet shown
below) you will see that it consists of lots of waitForObject calls as parameters to various
other calls such as activateItem,
clickButton, mouseClick, and type. The waitForObject function waits until a GUI object
is ready to be interacted with (i.e., becomes visible and enabled), and
is then followed by some function that interacts with the object. The
typical interactions are activate (pop-up) a menu, click a menu option
or a button, or type in some text. (For a complete overview of
Squish's script commands see the User Guide (Chapter 13), the
API Reference Manual (Chapter 14), and the Tools Reference Manual (Chapter 15). Objects are
identified by names that Squish generates. (See How to Identify and Access Objects (Section 13.1) for full details.)
The generated code is about 30 lines of code. Here's an extract that just shows how Squish records clicking the Edit menu's Add option, typing in Jane Doe's details into the Add dialog, and clicking OK at the end to close the dialog and update the table.
![]() | Scripting Language Support |
|---|---|
Although the screenshots only show the Python test suite in action, for the code snippets quoted here and throughout the tutorial, we show the code for all the scripting languages that Squish supports. In practice you would normally only use one of them of course, so feel free to just look at the snippets in the language you are interested in and skip the others. (In the HTML version of this manual you can use the combobox at the top of the page to select the language you use—this will hide the code snippets in other languages.) |
activateItem(waitForObjectItem(":Address Book - MyAddresses.adr_org.eclipse.swt.widgets.Menu", "Edit"))
activateItem(waitForObjectItem(":Edit_org.eclipse.swt.widgets.Menu", "Add..."))
type(waitForObject(":Address Book - Add.Forename:_org.eclipse.swt.widgets.Text"), "Jane")
type(waitForObject(":Address Book - Add.Surname:_org.eclipse.swt.widgets.Text"), "<Tab>")
type(waitForObject(":Address Book - Add.Surname:_org.eclipse.swt.widgets.Text"), "Doe")
type(waitForObject(":Address Book - Add.Email:_org.eclipse.swt.widgets.Text"), "<Tab>")
type(waitForObject(":Address Book - Add.Email:_org.eclipse.swt.widgets.Text"), "jane.doe@nowhere.com")
type(waitForObject(":Address Book - Add.Phone:_org.eclipse.swt.widgets.Text"), "<Tab>")
type(waitForObject(":Address Book - Add.Phone:_org.eclipse.swt.widgets.Text"), "555 123 4567")
clickButton(waitForObject(":Address Book - Add.OK_org.eclipse.swt.widgets.Button"))
activateItem(waitForObjectItem(":Address Book - MyAddresses.adr_org.eclipse.swt.widgets.Menu", "Edit"));
activateItem(waitForObjectItem(":Edit_org.eclipse.swt.widgets.Menu", "Add..."));
type(waitForObject(":Address Book - Add.Forename:_org.eclipse.swt.widgets.Text"), "Jane");
type(waitForObject(":Address Book - Add.Surname:_org.eclipse.swt.widgets.Text"), "<Tab>");
type(waitForObject(":Address Book - Add.Surname:_org.eclipse.swt.widgets.Text"), "Doe");
type(waitForObject(":Address Book - Add.Email:_org.eclipse.swt.widgets.Text"), "<Tab>");
type(waitForObject(":Address Book - Add.Email:_org.eclipse.swt.widgets.Text"), "jane.doe@nowhere.com");
type(waitForObject(":Address Book - Add.Phone:_org.eclipse.swt.widgets.Text"), "<Tab>");
type(waitForObject(":Address Book - Add.Phone:_org.eclipse.swt.widgets.Text"), "555 123 4567");
clickButton(waitForObject(":Address Book - Add.OK_org.eclipse.swt.widgets.Button"));
activateItem(waitForObjectItem(":Address Book - MyAddresses.adr_org.eclipse.swt.widgets.Menu", "Edit"));
activateItem(waitForObjectItem(":Edit_org.eclipse.swt.widgets.Menu", "Add..."));
type(waitForObject(":Address Book - Add.Forename:_org.eclipse.swt.widgets.Text"), "Jane");
type(waitForObject(":Address Book - Add.Surname:_org.eclipse.swt.widgets.Text"), "<Tab>");
type(waitForObject(":Address Book - Add.Surname:_org.eclipse.swt.widgets.Text"), "Doe");
type(waitForObject(":Address Book - Add.Email:_org.eclipse.swt.widgets.Text"), "<Tab>");
type(waitForObject(":Address Book - Add.Email:_org.eclipse.swt.widgets.Text"), "jane.doe\@nowhere.com");
type(waitForObject(":Address Book - Add.Phone:_org.eclipse.swt.widgets.Text"), "<Tab>");
type(waitForObject(":Address Book - Add.Phone:_org.eclipse.swt.widgets.Text"), "555 123 4567");
clickButton(waitForObject(":Address Book - Add.OK_org.eclipse.swt.widgets.Button"));
activateItem(waitForObjectItem(":Address Book - MyAddresses.adr_Menu", "Edit"))
activateItem(waitForObjectItem(":Edit_org.eclipse.swt.widgets.Menu", "Add..."))
type(waitForObject(":Address Book - Add.Forename:_Text"), "Jane")
type(waitForObject(":Address Book - Add.Surname:_Text"), "<Tab>")
type(waitForObject(":Address Book - Add.Surname:_Text"), "Doe")
type(waitForObject(":Address Book - Add.Email:_Text"), "<Tab>")
type(waitForObject(":Address Book - Add.Email:_Text"), "jane.doe@nowhere.com")
type(waitForObject(":Address Book - Add.Phone:_Text"), "<Tab>")
type(waitForObject(":Address Book - Add.Phone:_Text"), "555 123 4567")
clickButton(waitForObject(":Address Book - Add.OK_Button"))
invoke activateItem [waitForObjectItem ":Address Book - MyAddresses.adr_org.eclipse.swt.widgets.Menu" "Edit"]
invoke activateItem [waitForObjectItem ":Edit_org.eclipse.swt.widgets.Menu" "Add..."]
invoke type [waitForObject ":Address Book - Add.Forename:_org.eclipse.swt.widgets.Text"] "Jane"
invoke type [waitForObject ":Address Book - Add.Surname:_org.eclipse.swt.widgets.Text"] "<Tab>"
invoke type [waitForObject ":Address Book - Add.Surname:_org.eclipse.swt.widgets.Text"] "Doe"
invoke type [waitForObject ":Address Book - Add.Email:_org.eclipse.swt.widgets.Text"] "<Tab>"
invoke type [waitForObject ":Address Book - Add.Email:_org.eclipse.swt.widgets.Text"] "jane.doe@nowhere.com"
invoke type [waitForObject ":Address Book - Add.Phone:_org.eclipse.swt.widgets.Text"] "<Tab>"
invoke type [waitForObject ":Address Book - Add.Phone:_org.eclipse.swt.widgets.Text"] "555 123 4567"
invoke clickButton [waitForObject ":Address Book - Add.OK_org.eclipse.swt.widgets.Button"]
As you can see the tester used the keyboard to tab from one text field to another and clicked the OK button with the mouse. If the tester had moved the focus by clicking the mouse and clicked the OK button by tabbing to it and pressing Spacebar, or any other combination of interactions, the outcome would be the same, but of course Squish will have recorded the actual actions that were taken.
![]() | Note |
|---|---|
In the code snippet it looks as though Squish is having to look up the
same objects by name time after time. In fact Squish cache's names, so
after a name has been looked up once, subsequent lookups are very fast.
Another thing to notice is that there are no explicit delays. (It is
possible to force a delay using Squish's Another point to notice is that all the object names begin with a colon. This identifies them as symbolic names. Squish supports several naming schemes, all of which can be used—and mixed—in scripts. The advantage of using symbolic names is that if the application changes in a way that results in different names being generated, we can simply update Squish's Object Map (which relates symbolic names to real names), and thereby avoid the need to change our test scripts. (See the Object Map (Section 15.10) and the Object Map view (Section 16.2.9) for more about the Object Map.) |
Now that we have seen how to record and play back a test and have seen the code that Squish generates, let's go a step further and make sure that at particular points in the test's execution certain conditions hold.