Table of Contents
Learn how to test Flex applications.
Table of Contents
This tutorial will show you how to create, run, and modify tests for an example Flex application. In the process you will learn about Squish's most frequently used features so that by the end of the tutorial you will be able to start writing your own tests for your own applications.
This chapter presents most of the major concepts behind Squish and provides the information you need to get started using Squish for testing your own applications. This tutorial does not discuss all of Squish's features, and those that it does cover are not covered in full detail. After reading this tutorial we recommend reading the User Guide (Chapter 5), and at least skimming the API Reference Manual (Chapter 6) and the Tools Reference Manual (Chapter 7), so that you are familiar with all the features that Squish has to offer, even if you don't need to use them all straight away.
This tutorial is divided into several sections. If you are new to Squish it is best to read all of them. If you are already using Squish you might want to just skim the tutorial, stopping only to read those sections that cover any new features that you haven't used before—or you could just skip straight to the User Guide (Chapter 5).
Whenever we show how to achieve something using the IDE we will always follow with an explanation of how to do the same thing using the command line tools. Using an IDE is the easiest and best way to start, but once you build up lots of tests you will want to automate them, (e.g., doing nightly runs of your regression test suite), so it is worth knowing how to use the command line tools since they can be run from batch files or shell scripts.
For this chapter we will use a simple Address Book application as our
AUT. The application is shipped with Squish in
SQUISHDIR/examples/flex/addressbook
. This is a very
basic application that allows users to interact with a fake existing address book or
create a new one, and add, edit, and remove entries.
Despite the application's simplicity,
it has all the key features that most standard flex applications have:
buttons, radio buttons, line edits, pop-up dialogs, and a central area—in this
case showing a table. All the ideas and practices that you
learn to test this application can easily be adapted to your own
applications. And naturally, the User Guide (Chapter 5) has many
more examples and shows how to test lots of flex-specific features,
as well as all the standard
editing widgets.
The screenshots show the application in action.
The Flex AddressBook.html
example.
![]() | Using the Examples |
---|---|
This tutorial's example is a HTML and Flex application
contained in the files
|
![]() | Additional installation steps |
---|---|
Squish for Flex requires the installation of a special version of the Flash plugin to be able to hook into the Flex application. Please see Installation for Flex applets in web pages (Section 3.8) for detailed instructions on where to find that debug version of the plugin as well as how to install is. |
![]() | Windows Security Dialog |
---|---|
If you run |
![]() | Firefox Users |
---|---|
Unfortunately due to a technical limitation it is not possible to record or play back tests on Firefox if the browser is already running. (If you try a new tab will appear in Firefox but the test won't run.) The solution is to close Firefox; then, when you record or play back a test, Squish will start and close Firefox automatically as needed. |
In the following sections we will create a test suite and then create some tests, but first we will very briefly review some key Squish concepts.
To perform testing, two things are required:
an application to test—known as the Application Under Test (AUT), and
a test script that exercises the AUT.
One fundamental aspect of Squish's approach is that the AUT and the test script that exercises it are always executed in two separate processes. This ensures that even if the AUT crashes, it should not crash Squish. (In such cases the test script will fail gracefully and log an error message.) In addition to insulating Squish and test scripts from AUT crashes, running the AUT and the test script in separate processes brings other benefits. For example, it makes it easier to store the test scripts in a central location, and it also makes it possible to perform remote testing on different machines and platforms. The ability to do remote testing is particularly useful for testing AUTs that run on multiple platforms, and also when testing AUTs that run on embedded devices.
Squish runs a small server (squishserver) that handles the communication between the AUT and the test script. The test script is executed by the squishrunner tool, which in turn connects to the squishserver. The squishserver starts the AUT and injects the Squish hook into it. The hook is a small library that makes the AUT's live running objects accessible and that can communicate with the squishserver. With the hook in place, the squishserver can query AUT objects regarding their state and can execute commands—all on behalf of the squishrunner. And the squishrunner itself requests that the AUT performs whatever actions the test script specifies. All the communication takes place using network sockets which means that everything can be done on a single machine, or the test script can be executed on one machine and the AUT can be tested over the network on another machine.
The following diagram illustrates how the individual Squish tools work together.
From the test engineer's perspective this separation is not noticeable, since all the communication is handled transparently behind the scenes.
Tests can be written and executed using the Squish IDE, in which case the squishserver is started and stopped automatically, and the test results are displayed in the Squish IDE's Test Results view (Section 8.2.18). The following diagram illustrates what happens behind the scenes when the Squish IDE is used.
The Squish tools can also be used from the command line without the Squish IDE—this is useful for those testers who prefer to use their own tools (for example, their favorite editor), and also for performing automatic batch testing (for example, when running regression tests overnight). In these cases, the squishserver must be started manually, and stopped when all the testing is complete (or, if preferred, started and stopped for each test).
For Squish to make it possible for test scripts to be able to query and control an AUT, Squish must be able to access the AUT's internals, and this is made possible by the use of bindings. Bindings are in effect libraries that provide access to the objects—and in turn to the objects' properties and methods—that are available from a GUI toolkit, or from the AUT itself.
There are two sets of bindings that are of interest when developing tests using Squish.
GUI toolkit bindings—Squish provides bindings for all the GUI toolkits it supports, including Qt, Java AWT/Swing, Java SWT, Web, etc. This means that all the standard objects (including the GUI widgets) provided by these toolkits can be queried and controlled by Squish test scripts.
AUT-specific bindings—it is possible to create bindings that provide access to the AUT's own API for those cases where the toolkit's bindings don't provide sufficient functionality for proper testing. (Note that for Java- and Qt-based AUTs Squish automatically creates bindings to the AUTs objects—including custom classes; see How to Create and Access Application Bindings (Section 5.27).)
The need to make AUT-specific bindings is rarely needed in practice, but if it really is necessary, Squish provides a tool to make the process as simple as possible. The tool, squishidl (Section 7.4.5), is used to instrument the AUT (and any additional components) to generate AUT-specific bindings. The generated bindings library is seamlessly integrated with the standard GUI toolkit bindings and in the same way will automatically be loaded on demand by the Squish test tools.
When Squish automatically creates bindings to AUT classes, for Qt applications this means that the properties and slots of the AUT's custom widgets can be accessed without having to take any special action, and for Java AUTs this means that objects of custom classes are automatically available in test scripts without needing to be registered.
![]() | Terminology |
---|---|
The Squish documentation mostly uses the term widget when referring to GUI objects (i.e., buttons, menus, menu items, labels, table controls, etc). Windows users might be more familiar with the terms control and container, but here we use the term widget for both. Similarly, macOS users may be used to the term view; again, we use the term widget for this concept. |
In most cases, nothing special needs to be done to make an application testable, since the toolkit's API (e.g., Qt) provides enough functionality to implement and record test scripts. The connection to the squishserver is also established automatically, when the Squish IDE starts the AUT.
![]() | The Squish Directory |
---|---|
Throughout the manual, we often refer to the |
A test suite is a collection of one or more test cases (tests). Using a test suite is convenient since it makes it easy to share tests scripts and test data between tests.
Here, and throughout the tutorial, we will start by describing how to do things using the IDE, with the information for command line users following.
To begin with start up the Squish IDE, either by clicking or double-clicking the squishide icon, or by launching squishide from the taskbar menu or by executing squishide on the command line—whichever you prefer and that is suitable for the platform you are using. Once Squish starts up you might be greeted with a Welcome Page in case you're starting the squishide for the first time. Click the Workbench button in the upper right to dismiss it. Then, the squishide will look similar to the screenshot—but probably slightly different depending on the windowing system, colors, fonts, and theme that you use, and so on.
Once Squish has started click | to pop-up the New Squish Test Case wizard (Section 8.3.10) shown below.
Enter a name for your test suite and choose the folder where you want
the test suite to be stored. In the screenshot we have called the test
suite suite_py
and will put it inside the
addressbook
folder. (For your own tests you might
use a more meaningful name such as "suite_addressbook"; we chose
"suite_py" because for the sake of the tutorial we will create several
suites, one for each scripting language that Squish supports.)
Naturally, you can choose whatever name and folder you prefer. Once the
details are complete, click to go on to the
Toolkit (or Scripting Language) page.
If you get this wizard page, click the toolkit your AUT uses. For this example, we must click Web since we are testing a Flex application running in a Web browser. Then click
to go to the Scripting Language page.![]() | Scripting Languages |
---|---|
Squish supports several different scripting languages, and different installations may include support for some or all of these—so the scripting languages shown in the screenshot may be different from those shown by your version of Squish. |
Choose whichever scripting language you want—the only constraint is that you can only use one scripting language per test suite. (So if you want to use multiple scripting languages, just create multiple test suites, one for each scripting language you want to use.)
Having chosen a
scripting language, click Squish will create a sub-folder with
the same name as the test suite, and will create a file inside that
folder called suite.conf
that contains the test
suite's configuration details. The wizard will then close and Squish's IDE will
look similar to the screenshot below.
We are now ready to start creating tests. Read on to learn how to create test suites without using the IDE, or skip ahead to Recording Tests and Verification Points (Section 4.13.1.3) if you prefer.
![]() | For command-line users |
---|---|
To create a new test suite from the command line, two steps are necessary: create a directory for the test suite, and create a test suite configuration file.
|
We are now ready to record our first test.
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 made into existing test cases. We begin by
creating a New Script Test Case.
There are two ways we can do this. One way
is to click | . This will pop up the New Squish Test Case wizard (Section 8.3.10)—simply enter the name
for the test case and then click . Another
way is to click the 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, for example
test.py
. (If we had chosen JavaScript as our
scripting language the file would be called
test.js
, and correspondingly for Perl, Ruby, or Tcl.)
![]() | Note |
---|---|
![]()
If you get a sample |
To make the test script file (e.g., test.py
) appear
in an Editor view (Section 8.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 5). (In fact, two other function names are special
to Squish, cleanup
and init
; see Tester-Created Special Functions (Section 6.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 test scenario:
Add a new name and address.
Change the fourth name and address's surname field.
Remove the second name and address (this was the first one until we added a new one)
Verify that the first address is now the new one that was added.
We are now ready to record our first test. Click the ) that's to the right of the
tst_general
test case shown in the Test Suites view (Section 8.2.19)'s Test Cases list. This will cause
Squish to ask for the URL of the web application to test. The Flex addressbook
can be loaded by using http://localhost:9000/AddressBook.html
. If you used
a different port for the server.py
script adjust the URL accordingly.
Once the Browser is running perform the following actions—and don't worry about how
long it takes since Squish doesn't record idle time:
Click the Tab key to navigate between fields. Don't worry about typing mistakes—just backspace delete as normal and fix them. Finally, click the button. There should now be a new first address with the details you typed in.
button and fill in the form with forename, "Jane", surname "Doe", email address "jane.doe@nowhere.com", and a phone number of "555 123 4567". Click or press theClick the fourth row's first cell to select that record and then click the
button. In the form tab to or click the surname field and change the surname to "Doe". Finally, click the button. The change should be reflected in the list of addresses.Click the second row's first cell to select that record and then click the
button. Click the pop-up confirmation dialog's button. The change should be reflected in the list of addresses.Click the Squish Control Bar Window (Section 8.1.3) (the second button from the left) and select .
toolbar button in theThis will make the Squish IDE appear. In the Application
Objects view click the button, then in the
AUT move the mouse to the first forename (i.e., the "Jane" that you entered
earlier)—each flex element you move over should be highlighted with
a red outline. Once the correct table cell is highlighted (i.e.,
"Jane"), click it. Now go back to the Squish IDE and click the Properties view (Section 8.2.12)'s text
property.
Now click the button again, and this time
click the first surname showing in the AUT (i.e., "Doe"). Now go back to
the Squish IDE and again click the text
property.
Finally, click the button (at the
bottom of the Verification Point Creator view (Section 8.2.22))
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 (Section 8.1.3) and the AUT
will be back in view.
We've now completed the test, so click the Control Bar Window (Section 8.1.3)'s button (the left-most button).
Once the recording is finished, 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.
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.13.1.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 7.4.4) 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 Test Suites view (Section 8.2.19)). 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.
toolbar button (the green right-pointing triangle that appears when the test case is selected in theWhen 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, or the 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 --local |
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 type
and
clickButton
. 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 5), the
API Reference Manual (Chapter 6), and the Tools Reference Manual (Chapter 7). Objects are
identified by names that Squish generates. (See How to Identify and Access Objects (Section 5.1) for full details.)
![]() | 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.) |
The generated code is about 20 lines of code. Here's an extract that just shows how Squish records clicking the Add button, typing in Jane Doe's details into the Add form, and clicking Save at the end to close the form and update the table.
clickButton(waitForObject(":AddressBook.Add_Button")) type(waitForObject(":Add Entry_Edit"), "Jane") type(waitForObject(":Add Entry_Edit"), "<Tab>") type(waitForObject(":Add Entry_Edit_2"), "Doe") type(waitForObject(":Add Entry_Edit_2"), "<Tab>") type(waitForObject(":Add Entry_Edit_3"), "5551234567") type(waitForObject(":Add Entry_Edit_3"), "<Tab>") type(waitForObject(":Add Entry_Edit_4"), "jane.doe@nowhere.com") clickButton(waitForObject(":Add Entry.Ok_Button"))
clickButton(waitForObject(":AddressBook.Add_Button")); type(waitForObject(":Add Entry_Edit"), "Jane"); type(waitForObject(":Add Entry_Edit"), "<Tab>"); type(waitForObject(":Add Entry_Edit_2"), "Doe"); type(waitForObject(":Add Entry_Edit_2"), "<Tab>"); type(waitForObject(":Add Entry_Edit_3"), "5551234567"); type(waitForObject(":Add Entry_Edit_3"), "<Tab>"); type(waitForObject(":Add Entry_Edit_4"), "jane.doe@nowhere.com"); clickButton(waitForObject(":Add Entry.Ok_Button"));
clickButton(waitForObject(":AddressBook.Add_Button")); type(waitForObject(":Add Entry_Edit"), "Jane"); type(waitForObject(":Add Entry_Edit"), "<Tab>"); type(waitForObject(":Add Entry_Edit_2"), "Doe"); type(waitForObject(":Add Entry_Edit_2"), "<Tab>"); type(waitForObject(":Add Entry_Edit_3"), "5551234567"); type(waitForObject(":Add Entry_Edit_3"), "<Tab>"); type(waitForObject(":Add Entry_Edit_4"), "jane.doe\@nowhere.com"); clickButton(waitForObject(":Add Entry.Ok_Button"));
clickButton(waitForObject(":AddressBook.Add_Button")) type(waitForObject(":Add Entry_Edit"), "Jane") type(waitForObject(":Add Entry_Edit"), "<Tab>") type(waitForObject(":Add Entry_Edit_2"), "Doe") type(waitForObject(":Add Entry_Edit_2"), "<Tab>") type(waitForObject(":Add Entry_Edit_3"), "5551234567") type(waitForObject(":Add Entry_Edit_3"), "<Tab>") type(waitForObject(":Add Entry_Edit_4"), "jane.doe@nowhere.com") clickButton(waitForObject(":Add Entry.Ok_Button"))
invoke clickButton [waitForObject ":AddressBook.Add_Button"] invoke type [waitForObject ":Add Entry_Edit"] "Jane" invoke type [waitForObject ":Add Entry_Edit"] "<Tab>" invoke type [waitForObject ":Add Entry_Edit_2"] "Doe" invoke type [waitForObject ":Add Entry_Edit_2"] "<Tab>" invoke type [waitForObject ":Add Entry_Edit_3"] "5551234567" invoke type [waitForObject ":Add Entry_Edit_3"] "<Tab>" invoke type [waitForObject ":Add Entry_Edit_4"] "jane.doe@nowhere.com" invoke clickButton [waitForObject ":Add Entry.Ok_Button"]
The Add and Edit buttons are visible when when the AUT is showing the list of addresses. When the Add or Edit button is clicked a form is shown where a new address can be added or where the selected address can be edited. The form has Ok and Cancel buttons—when either of these are clicked the form is hidden and the list of addresses is shown once more.
![]() | Object Names |
---|---|
Notice 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 7.11) and the Object Map view (Section 8.2.10) 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.
In the previous section we saw how easy it is to insert verification
points during the recording of test scripts. Verification points can
also be inserted into existing test scripts, either by setting a
breakpoint and using the Squish IDE, or simply by editing a test script and
putting in calls to Squish's test functions such as test.compare
and test.verify
.
Squish supports four kinds of verification points: those that verify that object properties have particular values—known as "Object Property Verifications"; those that verify that an entire table has the contents we expect—known as "Table Verifications"; those that verify that two images match—known as "Screenshot Verifications"; and a hybrid verification type that includes properties and screenshots from multiple objects, known as "Visual Verifications". The most commonly used kind is object property verifications, and it is these that we will cover in the tutorial. For further reading, see How to Create and Use Verification Points (Section 5.22)).
Regular (non-scriptified) property
verification points are stored as XML files in the test case or test suite
resources, and contain the value(s) that need to be passed to
test.compare()
. These verification points can be reused across test
cases, and can verify many values in a single line of script code.
Scriptified property verification points are direct
calls to the test.compare
function, with two
arguments—the value of a particular property for a particular
object, and an expected value. We can manually insert calls to the test.compare
function in a recorded or hand
written script, or we can get Squish to insert them for us using scriptified
verification points. In the previous section we showed how to use the Squish IDE to insert
verifications during recording. Here we will first show how to use the
Squish IDE to insert verifications into an existing test script, and then we
will show how to insert a verification by hand.
Before asking Squish to insert verification points, it is best to make
sure that we have a list of what we want to verify and when. There are many
potential verifications we could add to the tst_general
test case, but since our concern here is simply to show how to do it, we
will only do two—we will verify that the "Jane Doe"
entry's email address and phone number match the ones entered, and put
the verifications immediately before the ones we inserted during
recording.
To insert a verification point using the IDE we start by putting a break point in the script (whether recorded or manually written—it does not matter to Squish), at the point where we want to verify.
The Squish IDE showing the tst_general test case with a breakpoint
As the above screenshot shows, we have set a breakpoint at line 22. This is done simply by right-clicking the line number and then clicking the Squish IDE during recording. Our additional verifications will precede them. (Note that your line number may be different if you recorded the test in a different way, for example, using keyboard shortcuts rather than clicking menu items.)
menu item in the context menu. We chose this line because it follows the script lines where the first address is removed, so at this point (just before finishing), the first address should be that of "Jane Doe". The screenshot shows the verifications that were entered using theHaving set the breakpoint, we now run the test as usual by clicking the Squish's main window will reappear (which will probably obscure the AUT). At this point the Squish IDE will automatically switch to the Squish Test Debugging Perspective (Section 8.1.2.3).
button, or by clicking the | menu option. Unlike a normal test run the test will stop when the breakpoint is reached (i.e., at line 22, or at whatever line you set), and![]() | Perspectives and Views |
---|---|
The Squish IDE works just like the Eclipse IDE. If you aren't used to Eclipse it is crucial to understand one key concept: Views and Perspectives. In Eclipse (and therefore in the Squish IDE), a View is essentially a child window (perhaps a dock window, or a tab in an existing window). And a Perspective is a collection of Views arranged together. Both are accessible through the menu. The Squish IDE is supplied with three Perspectives—the Squish Test Management Perspective (Section 8.1.2.2) (which is the Perspective that the Squish IDE starts with, and the one we have seen in all previous screenshots), Squish Test Debugging Perspective (Section 8.1.2.3), and Squish Spy Perspective (Section 8.1.2.1). You can change these Perspectives to include additional Views (or to get rid of any Views that you don't want), and you can create your own Perspectives with exactly the Views you want. So if your windows change dramatically it just means that the Perspective changed; you can always use the menu to change back to the Perspective you want. In practice, Squish will automatically change perspective to reflect the current situation, so it isn't really necessary to change perspective manually. |
As the screenshot below shows, when Squish stops at a breakpoint the Squish IDE automatically changes to the Squish Test Debugging Perspective (Section 8.1.2.3). The perspective shows the Variables view (Section 8.2.21), the Editor view (Section 8.2.6), the Debug view (Section 8.2.5), the Application Objects view (Section 8.2.1), and the Properties view (Section 8.2.12), Methods view (Section 8.2.9), and Test Results view (Section 8.2.18).
To insert a verification point we can expand items in the Application Objects view until we find the object we want to verify or we can use the
toolbar button to pick the object we are interested in in the AUT.The normal Squish Test Management Perspective (Section 8.1.2.2) can be returned to at any time by choosing it from the menu (or by clicking its toolbar button), although the Squish IDE will automatically return to it if you stop the script or run it to completion.
In this example we want to verify the first row's email address and
phone number (since we already have verifications for the forename and
surname). It is easiest to find them in the AUT rather than navigate the
Application Objects view (Section 8.2.1). First click the
toolbar button then click the first row's
email entry (i.e., “jane.doe@nowhere.com”) in the AUT. Now
back in the Squish IDE in the Properties view (Section 8.2.12) check
the text
property.
At this point the verification point has not been added to the test script. We could easily add it by clicking the button. But before doing that we'll add one more thing to be verified.
Click the Squish IDE in the Properties view (Section 8.2.12) check the innerText
property. Now both verifications will appear in the Verification Point Creator view (Section 8.2.22) as the screenshot shows.
We have now said that we expect these properties to have the values shown, that is, an email address of “jane.doe@nowhere.com” and phone number of “555 123 4567”. We must click the button to actually insert the verification point, so do that now.
We don't need to continue running the test now, so we can either stop running the test at this point (by clicking the
toolbar button), or we can continue (by clicking the button).Once we have finished inserting verifications and stopped or finished running the test we should now disable the break point. Just right click the break point and click the Squish inserted to perform the verifications—notice that the code is structurally identical to the code inserted during recording.)
menu option in the context menu. We are now ready to run the test without any breakpoints but with the verification points in place. Click the button. This time we will get some additional test results—as the screenshot shows—one of which we have expanded to show its details. (We have also selected the lines of code thatThese particular verification points generate four tests comparing the forename, surname, email, and phone number of the newly inserted entry.
Another way to insert verification points is to insert them in code
form. In theory we can just add our own calls to Squish's test
functions such as test.compare
and test.verify
anywhere we like in an existing
script. In practice it is best to make sure that Squish knows about
the objects we want to verify first so that it can find them when the
test is run. This involves a very similar procedure to inserting them using the Squish IDE.
First we set a breakpoint where we intend adding our verifications. Then
we run the test script until it stops. Next we navigate in the
Application Objects view (Section 8.2.1) until we find the
object we want to verify—or we click the
toolbar button and click the object in the
AUT. At this point it is wise to right-click the
object we are interested in and click the context menu option. This will ensure that Squish
can access the object. Then right click again and click the
context
menu option—this gives us the name of the object that Squish
will use to identify it. Now we can edit the test script to add in our
own verification and finish or stop the execution. (Don't forget to
disable the break point once it isn't needed any more.)
Although we can write our test script code to be exactly the same style as the automatically generated code, it is usually clearer and easier to do things in a slightly different style, as we will explain in a moment.
For our manual verifications we want to check the number of addresses
present in the <table>
after loading the
initial addresses, then after the new address is
added, and finally after the second address is removed.
When writing scripts by hand, we use Squish's test
module's functions to verify conditions at certain points during our
test script's execution. As the screenshot (and the code snippets below)
show, we begin by retrieving a reference to the object we are interested
in. Using the waitForObject
function is
standard practice for manually written test scripts. This function waits
for the object to be available (i.e., visible and enabled), and then
returns a reference to it. (Otherwise it times out and raises a
catchable exception.) We then use this reference to access the item's
properties—in this case the Table
's
rowCount
property—and verify that the value is what
we expect it to be using the test.verify
function. (Incidentally, we got the name for the object from the
previous line so we didn't need to set a breakpoint and manually add the
table's name to the Object Map to ensure that Squish would remember it
in this particular case because Squish had already added it during the
test recording.)
Here is the code we entered manually for the first verification for all
the scripting languages that Squish supports. Naturally, you only need
to look at the code for the language that you will be using for your own
tests. (For all the row count verifications we just did calls to the test.verify
function—or to the test.compare
function for Tcl since it's more
convenient.)
test.verify(table.rowCount == 125)
test.verify(table.rowCount == 125);
test::verify($table->rowCount == 125);
Test.verify(table.rowCount == 125)
test compare [property get $table rowCount] 125
The coding pattern is very simple: we retrieve a reference to the object we are interested in and then verify its properties using one of Squish's verification functions. And we can, of course, call methods on the object to interact with it if we wish.
We will see more examples of manually written code shortly, in the Creating Tests by Hand (Section 4.13.1.5) section, and further examples are in the User Guide (Chapter 5).
For complete coverage of verification points, see How to Create and Use Verification Points (Section 5.22) in the User Guide (Chapter 5).
After each test run finishes, the test results—including those for the verification points—are shown in the Test Results view at the bottom of the Squish IDE.
This is a detailed report of the test run and would also contain details of any failures or errors, etc. If you click on a Test Results item, the Squish IDE highlights the script line which generated the test result. And if you expand a Test Results item, you can see additional details of the test.
Now that we have seen how to record a test and modify it by inserting verification points, we are ready to see how to create tests manually. The easiest way to do this is to modify and refactor recorded tests, although it is also perfectly possible to create manual tests from scratch.
Potentially the most challenging part of writing manual tests is to use the right object names, but in practice, this is rarely a problem. We can either copy the symbolic names that Squish has already added to the Object Map when recording previous tests, or we can copy object names directly from recorded tests. And if we haven't recorded any tests and are starting from scratch we can use the Spy. We do this by clicking the toolbar button. This starts the AUT and switches to the Squish Spy Perspective (Section 8.1.2.1). We can then interact with the AUT until the object we are interested in is visible. Then, inside the Squish IDE we can navigate to the object in the Application Objects view—or use the toolbar button—and use the context menu to both add the object to the Object Map (so that Squish will remember it) and to the clipboard (so that we can paste it into our test script). And at the end we can click the toolbar button to terminate the AUT and return Squish to the Squish Test Management Perspective (Section 8.1.2.2). (See How to Use the Spy (Section 5.21.3) in the User Guide (Chapter 5) for more details on using the Spy.)
We can view the Object Map by clicking the Object Map view (Section 8.2.10)). Every application object that Squish interacts with is listed here, either as a top-level object, or as a child object (the view is a tree view). We can retrieve the symbolic name used by Squish in recorded scripts by right-clicking the object we are interested in and then clicking the context menu's Copy item. This is useful for when we want to modify existing test scripts or when we want to create test scripts from scratch, as we will see later on in the tutorial.
toolbar button (see also, theSquish's Object Map
Suppose we want to test the AUT's Add functionality by adding three new names and addresses. We could of course record such a test but it is just as easy to do everything in code. The steps we need the test script to do are: first click the New button (and then OK) to create a new address book, then for each new name and address, click the Add button, then fill in the details, and click Save. We also want to verify after clicking New that there are no rows of data and at the end that there are three rows. We will also refactor as we go, to make our code as neat and modular as possible.
First we must create a new empty test case. Click
tst_adding
. Squish will automatically create an
empty test.py
(or test.js
, and
so on) file.
Command line users can simply create a tst_adding
directory inside the test suite's directory and create and edit the
test.py
file (or test.js
and
so on) within that directory.
The first thing we need is a way to start the AUT. Here are the first
few lines from the recorded
tst_general
script:
def main(): loadUrl("http://localhost:9000/AddressBook.html") table = waitForObject(":AddressBook_Table") test.verify(table.rowCount == 125) clickButton(waitForObject(":AddressBook.Add_Button"))
function main() { loadUrl("http://localhost:9000/AddressBook.html"); var table = waitForObject(":AddressBook_Table"); test.verify(table.rowCount == 125); clickButton(waitForObject(":AddressBook.Add_Button"));
sub main { loadUrl("http://localhost:9000/AddressBook.html"); my $table = waitForObject(":AddressBook_Table"); test::verify($table->rowCount == 125); clickButton(waitForObject(":AddressBook.Add_Button"));
def main loadUrl("http://localhost:9000/AddressBook.html") table = waitForObject(":AddressBook_Table") Test.verify(table.rowCount == 125) clickButton(waitForObject(":AddressBook.Add_Button"))
proc main {} { invoke loadUrl "http://localhost:9000/AddressBook.html" set table [waitForObject ":AddressBook_Table"] test compare [property get $table rowCount] 125 invoke clickButton [waitForObject ":AddressBook.Add_Button"]
Notice that the pattern in the code is simple: start the AUT, then wait for the first object to be ready to interact with it.
![]() | Note |
---|---|
It may seem a waste to put our functions in
|
Sometimes the AUT will appear to freeze during test execution. When this happens, just wait for Squish to time out the AUT (about 20 seconds), and then it will pop up an Object Not Found dialog (Section 8.3.14), indicating an error like this:
Error Script Error Apr 9, 2011 Detail LookupError: Item 'New...' in object ':Address Book.File' not found or ready. Called from: C:\squish\examples\flex\addressbook\suite_js\tst_adding\test.js: 18 Location C:\squish\examples\flex\addressbook\suite_js\tst_adding\test.js:3
This usually means that Squish doesn't have an object with the given
name, or property values, in the Object Map. From here, we can
Pick a new object, Debug,
Throw Error or, after picking
a new object, Retry. In addition to the Spy's Object Picker () we
can use the Application Objects view (Section 8.2.1) to locate the
objects we are interested in and use a context menu action to
Add to the Object Map. In general, recording a dummy test
case that interacts with all of the relevant AUT objects is a much more
efficient way to initially populate the Object Map.
We've spent a bit of time on the issue of naming since it is probably the part of writing scripts that leads to the most error messages (usually of the "object ... not found" kind shown above.) Once we have identified the objects we are going to access in our tests, writing test scripts using Squish is very straightforward. And of course you can almost certainly use the scripting language you are most familiar with since Squish supports the most popular ones available.
We are now almost ready to write our own test script. It is probably
easiest to begin by recording a dummy test. So click
tst_dummy
. Then click the dummy test case's
toolbar button
().
Once the AUT starts, click the New button. This will clear
out the example data and leave the table empty and ready for new data.
Click the Control Bar Window (Section 8.1.3)'s button. Replay this test just to confirm that
everything works okay. The sole purpose of this is to make sure that
Squish adds the necessary names to the Object Map since it is probably
quicker to do it this way than to use the Spy for every object of
interest. After replaying the dummy test you can delete it if you want
to.
With all the object names we need in the Object Map we can now write our
own test script completely from scratch. We will start with the
main
function, and then we will look at the supporting
functions that the main
function uses.
def main(): loadUrl("http://localhost:9000/AddressBook.html") table = waitForObject(":AddressBook_Table") invokeMenuItem("File", "New") test.verify(table.rowCount == 0) data = [("Andy", "Beach", "andy.beach@nowhere.com", "555 123 6786"), ("Candy", "Deane", "candy.deane@nowhere.com", "555 234 8765"), ("Ed", "Fernleaf", "ed.fernleaf@nowhere.com", "555 876 4654")] for oneNameAndAddress in data: addNameAndAddress(oneNameAndAddress) test.verify(table.rowCount == len(data))
function main() { loadUrl("http://localhost:9000/AddressBook.html"); var table = waitForObject(":AddressBook_Table"); invokeMenuItem("File", "New"); test.verify(table.rowCount == 0); var data = new Array( new Array("Andy", "Beach", "andy.beach@nowhere.com", "555 123 6786"), new Array("Candy", "Deane", "candy.deane@nowhere.com", "555 234 8765"), new Array("Ed", "Fernleaf", "ed.fernleaf@nowhere.com", "555 876 4654")); for (var row = 0; row < data.length; ++row) addNameAndAddress(data[row]); test.verify(table.rowCount == data.length); }
sub main { loadUrl("http://localhost:9000/AddressBook.html"); my $table = waitForObject(":AddressBook_Table"); invokeMenuItem("File", "New"); test::verify($table->rowCount == 0); my @data = (["Andy", "Beach", "andy.beach\@nowhere.com", "555 123 6786"], ["Candy", "Deane", "candy.deane\@nowhere.com", "555 234 8765"], ["Ed", "Fernleaf", "ed.fernleaf\@nowhere.com", "555 876 4654"]); foreach $oneNameAndAddress (@data) { addNameAndAddress(@{$oneNameAndAddress}); } test::verify($table->rowCount == scalar(@data)) ; }
def main loadUrl("http://localhost:9000/AddressBook.html") table = waitForObject(":AddressBook_Table") invokeMenuItem("File", "New") Test.verify(table.rowCount == 0) data = [["Andy", "Beach", "andy.beach@nowhere.com", "555 123 6786"], ["Candy", "Deane", "candy.deane@nowhere.com", "555 234 8765"], ["Ed", "Fernleaf", "ed.fernleaf@nowhere.com", "555 876 4654"]] data.each do |oneNameAndAddress| addNameAndAddress(oneNameAndAddress) end Test.verify(table.rowCount == data.length) end
proc main {} { invoke loadUrl "http://localhost:9000/AddressBook.html" set table [waitForObject ":AddressBook_Table"] invokeMenuItem "File" "New" test compare [property get $table rowCount] 0 set data [list \ [list "Andy" "Beach" "andy.beach@nowhere.com" "555 123 6786"] \ [list "Candy" "Deane" "candy.deane@nowhere.com" "555 234 8765"] \ [list "Ed" "Fernleaf" "ed.fernleaf@nowhere.com" "555 876 4654"] ] for {set i 0} {$i < [llength $data]} {incr i} { addNameAndAddress [lindex $data $i] } test compare [property get $table rowCount] [llength $data] }
We begin by starting the AUT with a call to the startBrowser
function parametrized by the name of
ask us to confirm the page to load. Then we obtain a reference to the
Table
. We copied the name from the
Object Map into our code. The waitForObject
function waits until an object is ready (visible and enabled) and
returns a reference to it—or it times out and raises a catchable
exception. We have used a symbolic name to access
the table—these are the names that Squish uses when recording
tests—rather than a real/multi-property name (which we will soon
see an example of). It is best to use symbolic names where possible
because if any AUT object name changes, with symbolic names we just have
to update the Object Map (Section 7.11), without needing to change
our test code. Once we have the table
reference we can use
it to access any of the DataGridView
's
public methods and properties.
The invokeMenuItem
function is one we have created
specially for this test. It takes a menu name and a menu option name and
invokes the menu option. After using the invokeMenuItem
function to do
File|New, we verify that the table's row count is 0. The test.verify
function is useful when we simply
want to verify that a condition is true rather than compare two
different values. (For Tcl we usually use the test.compare
function rather than the test.verify
function simply because it is
slightly simpler to use in Tcl.)
Next, we create some sample data and call a custom
addNameAndAddress
function to populate the table with the
data using the AUT's Add dialog. And finally, we again compare the table's row
count, this time to the number of rows in our sample data.
We will now review each of the two supporting functions, so as to
cover all the code in the tst_adding
test case,
starting with the invokeMenuItem
function.
def invokeMenuItem(menu, item): menuName = "{container=':AddressBook.AddressBook_Window' text='%s' type='MenuBarItem'}" % menu mouseClick(waitForObject(menuName)) mouseClick(waitForObject("{container=%s text='%s' type='MenuItem'}" % (menuName, item)))
function invokeMenuItem(menu, item) { var menuName = "{container=':AddressBook.AddressBook_Window' text='"+menu+"' type='MenuBarItem'}"; mouseClick(waitForObject(menuName)); mouseClick(waitForObject("{container="+menuName+" text='"+item+"' type='MenuItem'}")); }
sub invokeMenuItem { my ($menu, $item) = @_; my $menuName = "{container=':AddressBook.AddressBook_Window' text='$menu' type='MenuBarItem'}"; mouseClick(waitForObject($menuName)); mouseClick(waitForObject("{container=$menuName text='$item' type='MenuItem'}")); }
def invokeMenuItem(menu, item) menuName = "{container=':AddressBook.AddressBook_Window' text='#{menu}' type='MenuBarItem'}" mouseClick(waitForObject(menuName)) mouseClick(waitForObject("{container=#{menuName} text='#{item}' type='MenuItem'}")) end
proc invokeMenuItem {menu item} { set menuName "{container=':AddressBook.AddressBook_Window' text='$menu' type='MenuBarItem'}" invoke mouseClick [waitForObject $menuName] invoke mouseClick [waitForObject "{container=$menuName text='$item' type='MenuItem'}"] }
Symbolic names always begin with a colon and embed various bits of
information about an object and its type. Real names are represented by
a brace enclosed list of space-separated key–value pairs. Every
real name must specify at least the type
property.
Here we've used the type
property
to uniquely identify the menubar (since the application only has one), and
the type
and text
properties to uniquely
identify the menu item.
Once we have identified the object we want to interact with we use the
waitForObject
function to retrieve a
reference to it and then we click it using the
mouseClick
function. The
waitForObject
function pauses Squish until
the specified object (and its item in the latter case) are visible and
enabled. So, here, we waited for the menu bar and one of its menu bar
items, and then we waited for a menu item. And as soon as the waiting is
over each time we click the object (or its item) using the mouseClick
function.
def addNameAndAddress(oneNameAndAddress): invokeMenuItem("Edit", "Add...") (forename, surname, email, phone) = oneNameAndAddress type(waitForObject(":Add Entry_Edit"), forename) type(waitForObject(":Add Entry_Edit"), "<Tab>") type(waitForObject(":Add Entry_Edit_2"), surname) type(waitForObject(":Add Entry_Edit_2"), "<Tab>") type(waitForObject(":Add Entry_Edit_3"), phone) type(waitForObject(":Add Entry_Edit_3"), "<Tab>") type(waitForObject(":Add Entry_Edit_4"), email) type(waitForObject(":Add Entry_Edit_4"), "<Return>")
function addNameAndAddress(oneNameAndAddress) { invokeMenuItem("Edit", "Add..."); var forename = oneNameAndAddress[0]; var surname = oneNameAndAddress[1]; var phone = oneNameAndAddress[3]; var email = oneNameAndAddress[2]; type(waitForObject(":Add Entry_Edit"), forename); type(waitForObject(":Add Entry_Edit"), "<Tab>"); type(waitForObject(":Add Entry_Edit_2"), surname); type(waitForObject(":Add Entry_Edit_2"), "<Tab>"); type(waitForObject(":Add Entry_Edit_3"), phone); type(waitForObject(":Add Entry_Edit_3"), "<Tab>"); type(waitForObject(":Add Entry_Edit_4"), email); type(waitForObject(":Add Entry_Edit_4"), "<Return>"); }
sub addNameAndAddress { my ($forename, $surname, $email, $phone) = @_; invokeMenuItem("Edit", "Add..."); type(waitForObject(":Add Entry_Edit"), $forename); type(waitForObject(":Add Entry_Edit"), "<Tab>"); type(waitForObject(":Add Entry_Edit_2"), $surname); type(waitForObject(":Add Entry_Edit_2"), "<Tab>"); type(waitForObject(":Add Entry_Edit_3"), $phone); type(waitForObject(":Add Entry_Edit_3"), "<Tab>"); type(waitForObject(":Add Entry_Edit_4"), $email); type(waitForObject(":Add Entry_Edit_4"), "<Return>"); }
def addNameAndAddress(oneNameAndAddress) invokeMenuItem("Edit", "Add...") (forename, surname, email, phone) = oneNameAndAddress type(waitForObject(":Add Entry_Edit"), forename) type(waitForObject(":Add Entry_Edit"), "<Tab>") type(waitForObject(":Add Entry_Edit_2"), surname) type(waitForObject(":Add Entry_Edit_2"), "<Tab>") type(waitForObject(":Add Entry_Edit_3"), phone) type(waitForObject(":Add Entry_Edit_3"), "<Tab>") type(waitForObject(":Add Entry_Edit_4"), email) type(waitForObject(":Add Entry_Edit_4"), "<Return>") end
proc addNameAndAddress {oneNameAndAddress} { invokeMenuItem "Edit" "Add..." set forename [lindex $oneNameAndAddress 0] set surname [lindex $oneNameAndAddress 1] set email [lindex $oneNameAndAddress 2] set phone [lindex $oneNameAndAddress 3] invoke type [waitForObject ":Add Entry_Edit"] $forename invoke type [waitForObject ":Add Entry_Edit"] "<Tab>" invoke type [waitForObject ":Add Entry_Edit_2"] $surname invoke type [waitForObject ":Add Entry_Edit_2"] "<Tab>" invoke type [waitForObject ":Add Entry_Edit_3"] $phone invoke type [waitForObject ":Add Entry_Edit_3"] "<Tab>" invoke type [waitForObject ":Add Entry_Edit_4"] $email invoke type [waitForObject ":Add Entry_Edit_4"] "<Return>" }
For each set of name and address data we click the Add button to make
the Add form visible. Then for each value received we populate the
appropriate field by waiting for the relevant text
field to be
ready and then typing in the text using the type
function.
And at the end we click the form's Save button. We got the line at the
heart of the function by copying it from the recorded
tst_general
test and simply parametrizing it by
the field name and text. Similarly, we copied the code for clicking the
Ok button from the tst_general
test case's code.
The entire test is around 30 lines of code—and would be even less
if we put some of the common functions (such as
invokeMenuItem
and addNameAndAddress
) in
a shared script. And much of the code was copied directly from the
recorded test, and in some cases parametrized.
This should be sufficient to give a flavor of writing test scripts for an AUT. Keep in mind that Squish provides far more functionality than we used here, (all of which is covered in the API Reference Manual (Chapter 6) and the Tools Reference Manual (Chapter 7)). And Squish also provides access to the entire public APIs of the AUT's objects.
However, one aspect of the test case is not very satisfactory. Although
embedding test data as we did here is sensible for small amounts, it is
rather limiting, especially when we want to use a lot of test data.
Also, we didn't test any of the data that was added to see if it
correctly ended up in the Table
.
In the next section we will create a new version of this test, only this
time we will pull in the data from an external data source, and check
that the data we add to the Table
is
correct.
In the previous section we put three hard-coded names and addresses in
our test. But what if we want to test lots of data?
Or what if we want to change the data without having to change our test
script's source code. One approach is to import a dataset into Squish
and use the dataset as the source of the values we insert into our
tests. Squish can import data in .tsv
(tab-separated values format), .csv
(comma-separated values format), .xls
, or
.xlsx
(Microsoft® Excel™ spreadsheet formats).
[19]
Test data can either be imported using the Squish IDE, or manually using a file manager or console commands. We will describe both approaches, starting with using the Squish IDE.
For the addressbook application we want to
import the MyAddresses.tsv
data file. To do this we
must start by clicking
| to pop-up the Import Squish Resource dialog (Section 8.3.7). Inside the dialog click
the button to choose the file to
import—in this case MyAddresses.tsv
. Make
sure that the Import As combobox is set to
“TestData”. By default the Squish IDE will import the test data
just for the current test case, but we want the test data to be
available to all the test suite's test cases: to do this check the
radio button. Now
click the button. You can now see the file
listed in the Test Suite Resources view (in the Test Data tab), and if
you click the file's name it will be shown in an Editor view (Section 8.2.6). The screenshot shows Squish after the
test data has been added.
![]() | For command-line users |
---|---|
It is also possible to import test data outside the Squish IDE using a file
manager (such as File Explorer) or console commands. To do this, create
a directory inside the test suite's directory called
|
Squish with some imported test data
Although in real life we would modify our
tst_adding
test case to use the test data, for the
purpose of the tutorial we will make a new test case called
tst_adding_data
that is a copy of
tst_adding
and which we will modify to make use of
the test data.
The only function we have to change is main
, where
instead of iterating over hard-coded items of data, we iterate over all
the records in the dataset. We also need to update the expected row
count at the end since we are adding a lot more records now, and we will
also add a function to verify each record that's added.
def main(): loadUrl("http://localhost:9000/AddressBook.html") table = waitForObject(":AddressBook_Table") invokeMenuItem("File", "New") test.verify(table.rowCount == 0) limit = 10 # To avoid testing 100s of rows since that would be boring for row, record in enumerate(testData.dataset("MyAddresses.tsv")): forename = testData.field(record, "Forename") surname = testData.field(record, "Surname") email = testData.field(record, "Email") phone = testData.field(record, "Phone") addNameAndAddress((forename, surname, email, phone)) checkNameAndAddress(table, record) if row > limit: break test.verify(table.rowCount == row+1)
function main() { loadUrl("http://localhost:9000/AddressBook.html"); var table = waitForObject(":AddressBook_Table"); invokeMenuItem("File", "New"); test.verify(table.rowCount == 0); var limit = 10 // To avoid testing 100s of rows since that would be boring; var records = testData.dataset("MyAddresses.tsv"); for (var row = 0; row < records.length; ++row) { var record = records[row]; forename = testData.field(record, "Forename"); surname = testData.field(record, "Surname"); email = testData.field(record, "Email"); phone = testData.field(record, "Phone"); addNameAndAddress(new Array(forename, surname, email, phone)); checkNameAndAddress(table, record); if (row > limit) break; } test.verify(table.rowCount == row+1) }
sub main { loadUrl("http://localhost:9000/AddressBook.html"); my $table = waitForObject(":AddressBook_Table"); invokeMenuItem("File", "New"); test::verify($table->rowCount == 0); my $limit = 10; # To avoid testing 100s of rows since that would be boring; my @records = testData::dataset("MyAddresses.tsv"); my $row = 0; for (; $row < scalar(@records); ++$row) { my $record = $records[$row]; my $forename = testData::field($record, "Forename"); my $surname = testData::field($record, "Surname"); my $email = testData::field($record, "Email"); my $phone = testData::field($record, "Phone"); addNameAndAddress($forename, $surname, $email, $phone); checkNameAndAddress($table, $record); if ($row > $limit ) { last; } } test::verify($table->rowCount == $row+1) ; }
def main loadUrl("http://localhost:9000/AddressBook.html") table = waitForObject(":AddressBook_Table") invokeMenuItem("File", "New") Test.verify(table.rowCount == 0) limit = 10 # To avoid testing 100s of rows since that would be boring rows = 0 TestData.dataset("MyAddresses.tsv").each_with_index do |record, row| forename = TestData.field(record, "Forename") surname = TestData.field(record, "Surname") email = TestData.field(record, "Email") phone = TestData.field(record, "Phone") addNameAndAddress([forename, surname, email, phone]) checkNameAndAddress(table, record) rows += 1 break if row > limit end Test.compare(table.rowCount, rows) end
proc main {} { invoke loadUrl "http://localhost:9000/AddressBook.html" set table [waitForObject ":AddressBook_Table"] invokeMenuItem "File" "New" test compare [property get $table rowCount] 0 set limit 10 set data [testData dataset "MyAddresses.tsv"] set columns [llength [testData fieldNames [lindex $data 0]]] set row 0 for {} {$row < [llength $data]} {incr row} { set record [lindex $data $row] set forename [testData field $record "Forename"] set surname [testData field $record "Surname"] set email [testData field $record "Email"] set phone [testData field $record "Phone"] set details [list $forename $surname $email $phone] addNameAndAddress $details checkNameAndAddress $table $record if {$row > $limit} { break } } test compare [property get $table rowCount] [expr $row + 1] }
Squish provides access to test data through its testData
module's functions—here we used the testData.dataset
function to access the data file
and make its records available, and the testData.field
function to retrieve each record's
individual fields.
Having used the test data to populate the table we
want to be confident that the data in the table is the same as what we
have added, so that's why we added the
checkNameAndAddress
function. We also added a limit to
how many records we would compare, just to make the test run faster.
def checkNameAndAddress(table, record): for column in range(0, len(testData.fieldNames(record))): # Application always inserts new entries at the top cell = waitForObject("{container=':AddressBook_Table' row='0' column='%d' type='TableCell'}" % column) test.compare(cell.text, testData.field(record, column))
function checkNameAndAddress(table, record) { for (var column = 0; column < testData.fieldNames(record).length; ++column) { // Application always inserts new entries at the top; cell = waitForObject("{container=':AddressBook_Table' row='0' column='"+column+"' type='TableCell'}"); test.compare(cell.text, testData.field(record, column)); } }
sub checkNameAndAddress { my($table, $record) = @_; my @columnNames = testData::fieldNames($record); for (my $column = 0; $column <= scalar(@columnNames); $column++) { # Application always inserts new entries at the top; my $cell = waitForObject("{container=':AddressBook_Table' row='0' column='$column' type='TableCell'}"); test::compare($cell->text, testData::field($record, $column)); } }
def checkNameAndAddress(table, record) for column in 0..TestData.fieldNames(record).length-1 # Application always inserts new entries at the top cell = waitForObject("{container=':AddressBook_Table' row='0' column='#{column}' type='TableCell'}") Test.compare(cell.text, TestData.field(record, column)) end end
proc checkNameAndAddress {table record} { set columns [llength [testData fieldNames $record]] for {set column 0} {$column < $columns} {incr column} { # Application always inserts new entries at the top set cell [waitForObject "{container=':AddressBook_Table' row='0' column='$column' type='TableCell'}"] test compare [property get $cell text] [testData field $record $column] } }
The screenshot show Squish's Test Summary log after the data-driven tests have been run.
Squish after a successful data-driven test run
Squish can also do keyword-driven testing. This is a bit more sophisticated than data-driven testing. See How to Do Keyword-Driven Testing (Section 5.16).
We have now completed the tutorial! Squish can of course do much more than we have shown here, but the aim has been to get you started with basic testing as quickly and easily as possible. The User Guide (Chapter 5) provides many more examples, including those that show how tests can interact with particular input elements such as selects, select-ones, texts, and text-areas.
The API Reference Manual (Chapter 6) and Tools Reference Manual (Chapter 7) give full details of Squish's testing API and the numerous functions it offers to make testing as easy and efficient as possible. It is well worth reading the User Guide (Chapter 5) and at least skimming the API Reference Manual (Chapter 6) and Tools Reference Manual (Chapter 7)—especially since the time invested will be repaid because you'll know what functionality Squish provides out of the box and can avoid reinventing things that are already available.
[19]
Both .csv
and .tsv
files are
assumed to use the Unicode UTF-8 encoding—the same encoding used
for all test scripts.