Probably the most important issue to face testers when writing scripts
from scratch (or when modifying recorded scripts), is how to access
objects in the user interface. We can obtain a reference to an object
using the waitForObject function. This
function waits for the object to become visible and available and then
returns a reference to it, or raises a catchable exception if it times
out. If we need a reference to an object that isn't visible we must use
the findObject function, which does not
wait. Both these functions take an object name, but getting the right
name can be tricky, so we will explain the issues and solutions here
before going into the Squish edition-specific and scripting
language-specific details.
Squish supports four completely different naming schemes, “symbolic names”, “real names” (also known as “multi-property names”), “qualified names”, and “hierarchical” names. Symbolic names are used by Squish when recording scripts (except for Tk AUTs where qualified names are used). Except for Tk AUTs where we use qualified names, for hand-written code we can use symbolic names or real names. It is best to use symbolic names, although for some purposes real names are more convenient. Hierarchical names are supported for backward compatibility; they should not be used in new tests.
The easiest situation is where an application object has been given an explicit name by the programmer. For example, using the Qt toolkit, an object can be given a name like this:
cashWidget->setObjectName("CashWidget");
When an object is given a name in this way, we can identify it using a
real name that specifies just two properties: the object's type and its
object name. Here is how we can access the cashWidget label
in the various scripting languages using the waitForObject function:
cashWidget = waitForObject("{name='CashWidget' type='QLabel'}")
var cashWidget = waitForObject("{name='CashWidget' type='QLabel'}");
my $cashWidget = waitForObject("{name='CashWidget' type='QLabel'}");
set cashWidget [waitForObject "{name='CashWidget' type='QLabel'}"]
To create a string that represents a real (multi-property) name, we
create a string which has an opening brace, then one or more
space-separated property items (each having the form,
propertyname='value'),
and finally a closing brace. For most toolkits at least two properties
must be specified with one being the object's type. If the
object has an object name, using just the type and
name properties is sufficient (providing that the name is
unique amongst objects of the specified type).
Once we have a reference to an object we can access its properties, for example, to check them against expected values, or to change them. We will see how to do this in the Squish edition-specific sections that follow.
Unfortunately, reality is not often so convenient. Programmers may not give unique names to objects, or they might not set explicit names at all, and in any case some objects are created as a result of program execution rather than directly by programmers. Furthermore, most toolkits don't even allow objects to be named. Objects that don't have names are unnamed, and in most testing situations the majority of objects we want to test are unnamed. Squish has two solutions to this problem. One solution is an extension of the multiple-properties approach (real names), and the other is to use symbolic names.
See also, the Application Objects view (Section 16.2.1) and the Properties view (Section 16.2.11).
When we are faced with unnamed objects we can almost always uniquely
identify them by creating a name consisting of multiple properties. For
example, here is how we can identify and access a
payButton button:
payButtonName = ("{type='QPushButton' text='Pay' unnamed='1'"
"visible='1'}")
payButton = waitForObject(payButtonName)
var payButtonName = "{type='QPushButton' text='Pay' unnamed='1'" +
"visible='1'}";
var payButton = waitForObject(payButtonName);
my $payButtonName = "{type='QPushButton' text='Pay' unnamed='1'" .
"visible='1'}";
my $payButton = waitForObject($payButtonName);
set payButtonName {{type='QPushButton' text='Pay' unnamed='1'
visible='1'}}
set payButton [waitForObject $payButtonName]
This works because in this particular example there is only one button on the form with the text "Pay".
In some cases, the object we are interested in has neither a name nor any unique text of its own. But even in such cases it is usually possible to identify it. For example, an unnamed spinbox might well be the buddy of an associated label, so we can use this relationship to uniquely identify the spinbox as the following examples show:
paymentSpinBoxName = ("{buddy=':Make Payment.This Payment:_QLabel'"
"type='QSpinBox' unnamed='1' visible='1'}")
paymentSpinBox = waitForObject(paymentSpinBoxName)
var paymentSpinBoxName = "{buddy=':Make Payment.This Payment:_QLabel'" +
"type='QSpinBox' unnamed='1' visible='1'}";
var paymentSpinBox = waitForObject(paymentSpinBoxName);
my $paymentSpinBoxName = "{buddy=':Make Payment.This Payment:_QLabel'" .
"type='QSpinBox' unnamed='1' visible='1'}";
my $paymentSpinBox = waitForObject($paymentSpinBoxName);
set paymentSpinBoxName {{buddy=':Make Payment.This Payment:_QLabel' \
type='QSpinBox' unnamed='1' visible='1'}}
set paymentSpinBox [waitForObject $paymentSpinBoxName]
Here, the buddy is identified using a symbolic name copied from the Object Map (Section 15.9).
If there is no obvious way of identifying an object, either use Squish's Spy tool (How to Use the Spy (Section 13.19.3)) to get Squish to provide a suitable name, or record a quick throwaway test in which you interact with the object of interest and then look in the Object Map (Section 15.9) to see what real and symbolic names Squish used, and then use one of these names in your test code.
In some cases we might want to use a property whose text varies. For example, if we want to identify a window whose caption text changes depending on the window's contents. This is possible using Squish's sophisticated matching capabilities and is described later in Improving Object Identification (Section 15.8).
If the waitForObject function cannot find the
object with the given name a LookupError exception is
raised which if left uncaught leads to an error entry to be added to
Squish's log in the Test Results view (Section 16.2.15). This
is normally what we want since it probably means we mistyped one of the
property's values. However, if an object may exist only in some cases
(for example, if a particular tab of a tab widget is chosen), we can use
the object.exists function to check if an
object of the given name exists, and if it does to perform any tests we
want on it in that case. For example, in Python we could write this:
moreOptionsButtonName = "{type='QPushButton' name='More Options'}"
if object.exists(moreOptionsButtonName):
moreOptionsButton = waitForObject(moreOptionsButtonName)
clickButton(moreOptionsButton)
One advantage of this approach is that if the object does not exist the script finds out straight away. Compare it with this approach:
try:
moreOptionsButtonName = "{type='QPushButton' name='More Options'}"
moreOptionsButton = waitForObject(moreOptionsButtonName)
except LookupError:
pass # button doesn't exist so don't try to click it
else:
clickButton(moreOptionsButton)
This is potentially slower than using the object.exists function since the waitForObject function will wait for 20 seconds
(the default timeout, which can be changed by giving a second argument),
although both approaches are valid.
When Squish records a test it uses symbolic names to identify the
widgets. Some symbolic names are quite easy to understand, for example,
":fileNameEdit_QLineEdit", while others can be more
cryptic, for example,
":CSV Table - before.csv.File_QTableWidget"—this
symbolic name includes the window caption which shows the name of the
current file. Symbolic names are generated programmatically by Squish
although they can also be used in hand-written code, or when modifying
or using extracts from recorded tests.
Symbolic names have one major advantage over real names: if a property that a real name depends on changes (i.e., due to a change in the AUT), the real name will no longer be valid, and all uses of it in test scripts will have to be updated. But if a symbolic name has been used, the real name that the symbolic name refers to, (i.e., the name's properties and their values), can simply be updated in the Object Map, and no changes to tests are necessary. It is for this reason that it is almost always better to use symbolic names rather than real names whenever possible. (See Object Map (Section 15.9) and the Object Map view (Section 16.2.9).)
(Incidentally, Squish distinguishes between the two kinds of names by
the fact that symbolic names begin with a colon (:) while
real names are always enclosed in braces ({}).)