This section discusses how to split tests into multiple files and how to share, access and use, shared scripts and shared data files. (It is also possible to share an Object Map (Section 16.10); see Creating an Object Map (Section 16.10.2.1) for details.)
Each test case contains a default test script file called
test.py (Python) or
test.js (JavaScript) or
test.pl (Perl) or
test.tcl (Tcl), depending on which testing language
has been set for the test suite.
When creating test suites it often happens that many of the test cases require some common functionality. Squish makes it possible to create separate script files that contain the common functionality and which can be used by all the test cases that need it.
Shared scripts are located in the test suite's
shared/scripts folder.
To create a shared script, in the Squish IDE's Test Suites view (Section 17.2.15)'s Test Suite Resources section,
click the toolbar button.
This will create a new empty shared script called
script_1.py (or script_1.js,
and so on, depending on the scripting language in use). The script can
be renamed to something more meaningful by clicking it and invoking the
context menu and using the option. (In
the screenshot we have renamed the shared script
common.py since it contains common functionality.)

Once the shared script has been renamed, click (or double-click depending on your platform and settings) to show it in an Editor view (Section 17.2.5). You can now edit the script to add any shared functionality you require. (We will explain how to access a shared script's functionality further on, since it must be done in a Squish-specific way.)
It is also possible (although less common) to add a shared script that can only be used by a particular test case. To do this click the toolbar button in the Test Suites view (Section 17.2.15)'s Test Case Resources panel. And, of course, we can add as many shared script files to our test suites (and test cases) as we like.
If you want to import an existing script into a test suite's (or test case's) shared script's folder, click |. This will pop up the "Import Squish Resource" dialog (Section 17.3.3). Enter the name of the script file to import (or choose it using the button. Then set the Import As combobox item to “Script”. If you want the script to be shared by all of the test suite's test cases (rather than just the current one that is shown), check the radio button. (Clicking this radio button is recommended since it is usually more convenient to share scripts amongst all test cases.) Now click the button and the imported script will be available in the Squish IDE.
Once a shared script has been created, it can be used by any test-case-specific scripts that need it. Note though, that shared scripts are not usually imported using the language-specific import mechanism—after all, not all languages have such a mechanism, for example, JavaScript doesn't. Instead, the Squish API provides the necessary functions.
The standard way to locate a shared script file (or shared data file),
is to use the findFile function. The first
argument is the type of file, which for shared scripts should be
“scripts”. The second argument is the script's filename
(with no path). The function will search all the standard locations that
Squish uses (shared/scripts and
SQUISH_SCRIPT_DIR), and will return the filename
including its full path.
Once we have the full path to the shared script, we can include it in
our test case's script. This is done by evaluating the shared
script using the source function—this
means that it is in effect executed as if the actual text of the shared
script was in the test case at the point where we call the source function. After this is done, all the
objects created in the shared script—typically, classes and
functions—become accessible in the test case's script.
Here is an example where we want to share a script file called
address_utility.py (or
address_utility.js, etc., depending on the
scripting language being used), so that we can access a function inside
it—in this example, the
insertDummyNamesAndAddresses function—that populates an
addressbook AUT with some names and addresses so that there is some data
present for further tests to work on.
def main():
...
source(findFile("scripts", "address_utility.py"))
insertDummyNamesAndAddresses()
...
function main()
{
// ...
source(findFile("scripts", "address_utility.js"));
insertDummyNamesAndAddresses();
// ...
}
sub main
{
# ...
source(findFile("scripts", "address_utility.pl"));
insertDummyNamesAndAddresses();
# ...
}
proc main {} {
# ...
source [findFile scripts "address_utility.tcl"]
insertDummyNamesAndAddresses
# ...
}
Here we import the address_utility.py (or similar)
script which defines a function called
insertDummyNamesAndAddresses, which we are then able to
call. (You should already be familiar with this, since we used this
mechanism in several examples earlier in this User Guide.)
For scripting languages such as Python that support importing, it is
possible to use the language's standard import mechanism. However,
Squish's approach is usually more convenient, since in most cases our
shared scripts are only relevant to our tests. In general, for shared
scripts it is best to use Squish's source
function, but to import standard modules it is best to use the
language-specific mechanism (e.g., import in Python and
use in Perl).
Just as we have a test case script, we can also have test-case-specific
data files. Such files are stored in the test case's
testdata directory. However, in some cases, we want
to share the test data so that more than one test case can access it. In
this case we store the test data in the test suite's
shared/testdata directory.
Test data can be added through the Squish IDE by importing
files—Squish can read .tsv
(tab-separated values format), .csv
(comma-separated values format), and .xls
(Microsoft® Excel™ spreadsheet format). Or we can simply create the
directories on the command line in a console and copy our test data into
them. The techniques used for adding shared test data, whether using the
Squish IDE or manually, are exactly the same as for adding shared test
scripts, only we use the appropriate testdata
directory (rather than the scripts directory).
Although the top-level directory structure must follow what we have
described, within that structure—i.e., under
a testdata directory—you are free to
create subdirectories and structure them however you like.
Retrieving test data is done using the findFile function we mentioned earlier, only this
time the first argment must be “testdata”, and the second
argument the name of the test data file you want to access. Test data is
normally retrieved using Squish's data handling API; see Test Data Handling (Section 16.1.3.8).
Data-driven testing is an approach where the test data (input, and expected output), is kept separate from the test script code which only contains the test's logic. The normal practice is for the test data to be read from a file or database one item or record at a time, and for the test script to use the data to test the AUT and then compare the results with those that are expected.
One benefit of this approach is that it makes it possible to modify a test without actually having to change the test case's code—instead we simply add more data which the test then reads and processes along with the rest of the data. This makes it possible to separate responsibility for creating tests between test engineers who have coding skills and those who don't. Those with coding skills can create test scripts and encode the test logic in them, and test engineers who don't have coding skills can create and edit the test data that the test scripts use to test the AUT.
Squish provides an API for handling test data (see Test Data Handling (Section 16.1.3.8)), that makes it easy to create data-driven
tests. Importing test data files was been discussed in the previous
section—here we will see how to use Squish's script API to read
and use test data, and will assume that the test data has already be
imported or copied into the appropriate testdata
directory.
Test data always contains data in a tabular format. Squish can read
files in .tsv (tab-separated values format),
.csv (comma-separated values format), and
.xls (Microsoft® Excel™ spreadsheet format).
In the case of .csv and .tsv
files, Squish assumes that they use the Unicode UTF-8
encoding—the same encoding that is used for test scripts. In
.tsv files, records are separated by new lines and
fields are separated by tabs. The first record is used to describe the
columns. Here is an example .tsv data
file—addresses.tsv—with tabs indicated
by “->” and newlines indicated by “$”
characters:
First Name->Last Name->Address->Email->Number$ Max->Mustermann->Bakerstreet 55->max@mustermann.net->1$ John->Kelly->Rhodeo Drv. 678->jkelly@acompany.com->2$ Joe->Smith->Queens Blvd. 37->joe@smith.com->3$
Each field (column) is separated by a tab, and each record (row, or
line) is separated by a newline. As is common practice with
.tsv (and .csv) files, the
first line is not data as such, but instead the field (column) names
(“First Name”, “Last Name”, etc.). All the
other lines are data records.
Here is an example where we read each record in turn and print its values to Squish's log:
for record in testData.dataset("addresses.tsv"):
firstName = testData.field(record, "First Name")
lastName = testData.field(record, "Last Name")
address = testData.field(record, "Address")
email = testData.field(record, "Email")
test.log("%s %s, %s; email: %s" % (
firstName, lastName, address, email))
var records = testData.dataset("addresses.tsv");
for (var row = 0; row < records.length; ++row) {
var record = records[row];
firstName = testData.field(record, "First Name");
lastName = testData.field(record, "Last Name");
address = testData.field(record, "Address");
email = testData.field(record, "Email");
test.log(firstName + " " + lastName + ", " + address +
"; email:" + email);
}
my @records = testData::dataset("addresses.tsv");
for (my $row = 0; $row < scalar(@records); $row++) {
my $record = $records[$row];
my $firstName = testData::field($record, "First Name");
my $lastName = testData::field($record, "Last Name");
my $address = testData::field($record, "Address");
my $email = testData::field($record, "Email");
test::log("$firstName $lastName, $address; email: $email");
}
set records [testData dataset "addresses.tsv"]
for {set row 0} {$row < [llength $data]} {incr row} {
set record [lindex $records $row]
set firstName [testData field $record "First Name"]
set lastName [testData field $record "Last Name"]
set address [testData field $record "Address"]
set email [testData field $record "Email"]
test log "$firstName $lastName, $address; email: $email"
}
Notice that we access fields by name, so the names we use in our test case's code must match those in the first line of the test data file.
In typical cases the test data file is found using the findFile function, and then the records are
retrieved using the testData.dataset
function. We then use the testData.field
function to access the contents of individual fields within a given
record.
By using a for loop we can iterate over every record in the
testdata—without having to know in advance how many records there
are, so the code is unaffected if records are removed or added. And of
course, in a realistic test we would feed the data to the AUT and
compare expected with actual results rather than simply printing the
data to the log as we have done here.
Most of the tutorials include a complete example of a data-driven test. For a Qt example, see Creating Data Driven Tests (Section 5.4.2); for a Java AWT/Swing example, see Creating Data Driven Tests (Section 6.4.2); for a Java SWT example, see Creating Data Driven Tests (Section 7.4.2); and for a Tk/Tcl example, see Creating Data Driven Tests (Section 12.4.2).
So far this section has only discussed using test data in test scripts to create data driven tests. But two other use cases arise in practice. One use case is where test data files are provided for the AUT to read, and another is where we want to retrieve files that the AUT has created during the test run so that they can be verified.
Let's start by looking at the first use case where we provide a data
file for the AUT to read. For example, imagine we are using the
addressbook AUT and we want it to load a file
called customers.adr at the start of the test
script so that it has a known set of data to work on. This is easily
achieved by storing the data file in the test case's
testdata directory—or in the test suite's
testdata directory, if we want more than one test
case to be able to access it.
We want to avoid hard-coding the path to the data file in our test
script since we want the flexibility to run our tests on different
machines, and even on different platforms. We can copy a data file into
the AUT's current working directory (without having to know its path)
using the testData.put function. We only
need to give the name of the file to this function since it will
automatically look in the test case's (and if not there, the test
suite's), testdata directory. Another benefit of
using this function is that once the test run has finished, Squish
will automatically clean up for us (i.e., Squish will delete the file
from the AUT's working directory).
Here is an example where we copy a test data file from the AUT's
testdata directory into the AUT's working
directory. Then we access the AUT's main window object
(Addressbook), and call that object's
fileOpen method with the name of the file we want it to
load.
def main():
...
testData.put("customers.adr")
findObject("Addressbook").fileOpen("customers.adr")
...
function main()
{
// ...
testData.put("customers.adr");
findObject("Addressbook").fileOpen("customers.adr");
// ...
}
sub main()
{
# ...
testData::put("customers.adr");
findObject("Addressbook")->fileOpen("customers.adr");
# ...
}
proc main {} {
# ...
invoke testData put "customers.adr"
[invoke [invoke findObject "Addressbook"] fileOpen "customers.adr"]
# ...
}
Another use case is where we need to verify that a file which has been
created by the AUT has the contents we expect. For example, let's assume
that during a test the addressbook AUT loads a data
file, customers.adr, performs various operations
(adds, edits, and deletes, addresses), and then saves its current
address data into a new file, edited-customers.adr.
After this has happened we want our test script to compare the
edited-customers.adr file with another file,
expected-customers.adr which has the contents we
expect the file to have after accounting for the changes to the data
made earlier in the script.
We can retrieve a file from the AUT's current directory using the testData.get function. Here's an example that
saves a file, retrieves the saved file and a file that should match the
saved file, and verifies that they match:
def main():
# load in customers.adr and add/edit/delete addresses
mainwindow = waitForObject("Addressbook")
mainwindow.saveAs("edited-customers.adr")
testData.get("edited-customers.adr")
data1 = open(findFile("testdata", "edited-customers.adr")).read()
data2 = open(findFile("testdata", "expected-customers.adr")).read()
test.compare(data1, data2)
function main()
{
// load in customers.adr and add/edit/delete addresses
var mainwindow = waitForObject("Addressbook");
mainwindow.saveAs("edited-customers.adr");
testData.get("edited-customers.adr");
var data1 = File.open(findFile("testdata",
"edited-customers.adr")).read();
var data2 = File.open(findFile("testdata",
"expected-customers.adr")).read();
test.compare(data1, data2);
}
sub main
{
# load in customers.adr and add/edit/delete addresses
my $mainwindow = waitForObject("Addressbook");
$mainwindow->saveAs("edited-customers.adr");
testData::get("edited-customers.adr");
open(FH1, findFile("testdata", "edited-customers.adr"));
open(FH2, findFile("testdata", "expected-customers.adr"));
my $data1 = join("", <F1>);
my $data2 = join("", <F2>);
test.compare($data1, $data2);
}
proc main {} {
# load in customers.adr and add/edit/delete addresses
set mainwindow [waitForObject "Addressbook"]
invoke $mainwindow saveAs "edited-customers.adr"
testData get "edited-customers.adr"
set data1 [read [open [findFile testdata "edited-customers.adr"]]]
set data2 [read [open [findFile testdata "expected-customers.adr"]]]
test compare $data1 $data2
}
We begin by getting a reference to the AUT's main window object; then we
call the main window's saveAs method to save the edited
data. Then we open both the newly saved data and the expected data
files, and read their entire contents. Finally, we compare the contents
of the files using the test.compare
function.
Squish's data handling API (see Test Data Handling (Section 16.1.3.8)) has
other useful functions. For example, if we only need to test that a file
has been created without concern for its contents we can call the testData.exists function. And if we want to
remove a file in the course of a test we can call the testData.remove function.