5.1. How to Identify and Access Objects

5.1.1. How to Access Named Objects
5.1.2. How to Access Objects Using Real (Multi-Property) Names
5.1.3. How to Access Objects Using Symbolic Names

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.

5.1.1. How to Access Named Objects

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:

Python

    cashWidget = waitForObject("{name='CashWidget' type='QLabel'}")

JavaScript

    var cashWidget = waitForObject("{name='CashWidget' type='QLabel'}");

Perl

    my $cashWidget = waitForObject("{name='CashWidget' type='QLabel'}");

Ruby

  cashWidget = waitForObject("{name='CashWidget' type='QLabel'}")

Tcl

    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 8.2.1) and the Properties view (Section 8.2.11).

5.1.2. How to Access Objects Using Real (Multi-Property) Names

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:

Python

    payButtonName = ("{type='QPushButton' text='Pay' unnamed='1'"
                     "visible='1'}")
    payButton = waitForObject(payButtonName)

JavaScript

    var payButtonName = "{type='QPushButton' text='Pay' unnamed='1'" +
                        "visible='1'}";
    var payButton = waitForObject(payButtonName);

Perl

    my $payButtonName = "{type='QPushButton' text='Pay' unnamed='1'" .
                        "visible='1'}";
    my $payButton = waitForObject($payButtonName);

Ruby

  payButtonName = "{type='QPushButton' text='Pay' unnamed='1'" +
  "visible='1'}"
  payButton = waitForObject(payButtonName)

Tcl

    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:

Python

    paymentSpinBoxName = ("{buddy=':Make Payment.This Payment:_QLabel'"
                          "type='QSpinBox' unnamed='1' visible='1'}")
    paymentSpinBox = waitForObject(paymentSpinBoxName)

JavaScript

    var paymentSpinBoxName = "{buddy=':Make Payment.This Payment:_QLabel'" +
                             "type='QSpinBox' unnamed='1' visible='1'}";
    var paymentSpinBox = waitForObject(paymentSpinBoxName);

Perl

    my $paymentSpinBoxName = "{buddy=':Make Payment.This Payment:_QLabel'" .
                             "type='QSpinBox' unnamed='1' visible='1'}";
    my $paymentSpinBox = waitForObject($paymentSpinBoxName);

Ruby

  paymentSpinBoxName = "{buddy=':Make Payment.This Payment:_QLabel'" +
  "type='QSpinBox' unnamed='1' visible='1'}"
  paymentSpinBox = waitForObject(paymentSpinBoxName)

Tcl

    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 7.11).

If there is no obvious way of identifying an object, either use Squish's Spy tool (How to Use the Spy (Section 5.21.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 7.11) 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 7.10).

If the waitForObject function cannot find the object with the given name an exception is raised. For most languages this is a base class exception, but for Python it is the more specialized LookupError and for Ruby Squish::LookupError. If the exception isn't caught an error entry to be added to Squish's log in the Test Results view (Section 8.2.15). (See How to Handle Exceptions Raised in Test Scripts (Section 5.19).) 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:

Python
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:
Python
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.

5.1.3. How to Access Objects Using Symbolic Names

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 7.11) and the Object Map view (Section 8.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 ({}).)