5.1. How to Identify and Access Objects

Table of Contents

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
5.1.4. How to Access Objects Using Images

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.

In cases where an object cannot be identified by name a purely visual search can be performed based on a sub-image.

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: type and name. Depending on the Object Map (Section 7.11) implementation used, the syntax for accessing the cashWidget label differs slightly.

Here are examples in the various scripting languages using the waitForObject function when using a Text-Based Object Map (Section 7.11.5):

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.

When using a Script-Based Object Map (Section 7.11.4), we are able to pass dictionaries to 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 [::Squish::ObjectName name CashWidget type QLabel ]]

With Script-Based object maps, a dictionary object is constructed which accepts the properties to match a given object against as a set of key-value pairs. See Script-Based Object Map API (Section 7.11.4.3) for more information on the available API for constructing object names.

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.

See also, the Application Objects view (Section 8.2.1) and the Properties view (Section 8.2.12).

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

Some objects are not identifiable by name, in which case, a special property, unnamed is 1. When we are faced with unnamed objects we can usually identify them using other properties. For example, here is how we can identify and access a payButton button when using legacy Text-Based Object Map (Section 7.11.5) object names.

Python
payButtonName = "{type='QPushButton' text='Pay' visible='1'}"
payButton = waitForObject(payButtonName)
JavaScript
    var payButtonName = "{type='QPushButton' text='Pay' visible='1'}";
    var payButton = waitForObject(payButtonName);
Perl
my $payButtonName = "{type='QPushButton' text='Pay' visible='1'}"
my $payButton = waitForObject($payButtonName);
Ruby
paybuttonName = "{type='QPushButton' text='Pay' visible='1'}"
payButton = waitForObject(payButtonName)
Tcl
set payButtonName {{type='QPushButton' text='Pay' visible='1'}}
set payButton [waitForObject $payButtonName]

Squish 6.4 introduces Script-Based Object Map (Section 7.11.4). The name above can be expressed as a key-value mapping in the native script language, as shown below:

Python

    payButtonName = {"text": "Pay", "type": "QPushButton", "visible": 1}
    payButton = waitForObject(payButtonName)
JavaScript

    var payButtonName = {'type':'QPushButton', 'text':'Pay', 'visible':'1'};
    var payButton = waitForObject(payButtonName);
Perl

    my $payButtonName = {'type'=>'QPushButton', 'text'=>'Pay', 'visible'=>'1'};
    my $payButton = waitForObject($payButtonName);
Ruby

    payButtonName = {:type=>'QPushButton', :text=>'Pay', :visible=>'1'}
    payButton = waitForObject(payButtonName)
Tcl

    set payButtonName [::Squish::ObjectName text Pay type QPushButton visible 1]
    set payButton [waitForObject $payButtonName] 

These names specify enough criteria for this particular GUI, such that there is only one button on the form with the text of 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. A Script-Based Object Map (Section 7.11.4) allows referencing other object names using plain script variables. Here, the buddy is identified using a symbolic name copied from the Object Map (Section 7.11).

Python

    paymentSpinBoxName = {"buddy": names.make_Payment_This_Payment_QLabel, 
                          "type": "QSpinBox", "unnamed": 1}
    paymentSpinBox = waitForObject(paymentSpinBoxName)
JavaScript

    var paymentSpinBoxName = {'buddy':names.makePaymentThisPaymentQLabel,
        'type':'QSpinBox', 'unnamed':'1'};
    var paymentSpinBox = waitForObject(paymentSpinBoxName);
Perl

    my $paymentSpinBoxName = {'buddy'=>$Names::make_payment_this_payment_qlabel, 
        'type'=>'QSpinBox', 'unnamed'=>'1'};
    my $paymentSpinBox = waitForObject($paymentSpinBoxName);
Ruby

    paymentSpinBoxName = {:buddy=>Names::Make_Payment_This_Payment_QLabel,
        :type=>'QSpinBox', :unnamed=>'1'}
    paymentSpinBox = waitForObject(paymentSpinBoxName)
Tcl

    set paymentSpinBoxName [::Squish::ObjectName type QSpinBox unnamed 1 \
        buddy $names::Make_Payment_This_Payment_QLabel ]
    set paymentSpinBox [waitForObject $paymentSpinBoxName]

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 put the mouse over the symbolic name, right-click and select Open Symbolic Name to see its real name in the Object Map (Section 7.11). You can use one or the other in your real test.

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.17). (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 (assuming an Script-Based Object Map (Section 7.11.4) is used):

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. Symbolic names assume different forms depending on whether the test uses a Text-Based Object Map (Section 7.11.5) or a Script-Based Object Map (Section 7.11.4): in the former case, symbolic names are plain strings starting with a :, in the latter case symbolic names are script variables. 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.10).)

5.1.4. How to Access Objects Using Images

Accessing objects by name as described above is the preferred approach. In case of dynamic user interfaces the selection of fitting properties may require some work but will result in the most robust test scripts. In light of changes in the application's GUI style for example.

In some cases, however, object identification cannot be tackled by properties. Not as easily at least. Such cases are:

  • Controls belonging to a custom or 3rd party UI toolkit.

  • Graphical shapes on a 2D or 3D canvas.

  • Access to applications other than the main AUT. This includes menus, task bars and desktop controls of the operating system itself.

To deal with such cases outside of the direct control Squish provides an alternative look-up based on sub-images. Images of arbitrary size and shape can be searched for on the screen with waitForImage. The determined location will be returned as a ScreenRectangle which can again serve as an input to interaction functions like mouseClick.

mouseClick(waitForImage("customcontrol.png"))

Creation of the needed sub-images as well as insertion of the respective script functions is most conveniently achieved interactively by using the Record Test Case action () and e.g. the Insert > mouseClick([Image]) action ().