When recording a test case or picking objects in the Spy, Squish automatically creates a name for each object that is accessed so that it can be identified later, for example, in a test script. Depending on the naming scheme that was chosen when the test suite was created, this name will either be a multi-property (real) name or a hierarchical name. Hierarchical names were used in earlier versions of Squish, and they are supported to ensure that old tests continue to run correctly. However, for new test suites the default (and recommended, and for some versions, only) naming scheme is multi-property (real) names since they lead to much more reliable object identification in the face of application changes.
Real names consist of a set of space-separated
<propertyName>=<value>
pairs enclosed in curly braces. For example, a multi-property name
which identifies the object whose type is Button and
whose text is “Hello” will look like this:
{type='Button' text='Hello'}
Every multi-property name must have at least two
properties, one of which is a mandatory toolkit-specific
property—the other property or properties can be any ones we like.
For Java AUTs the mandatory toolkit-specific property can be either the
type property or the basetype property (but
not both). For Web AUTs the mandatory toolkit-specific property is the
tagName property. And for all the other toolkits, the
mandatory toolkit-specific property is the type property.
Note that the type, basetype,
tagName properties (and a few others) can only be matched
exactly—most other properties can be matched using wildcards or
regexes to make Squish test scripts more flexible in the face of
changes. (See Improving Object Identification (Section 16.9).)
Many objects have lots of properties, and Squish must choose amongst them to create names that will correctly and uniquely identify each object, and that are as short as possible, i.e., that use as few properties as possible. Unfortunately, these two requirements conflict with each other: uniqueness can be ensured by using all or most of an object's properties, but short names require us to use as few properties as possible.
Why do we need short names at all? Because the fewer properties we use to identify an object, the less chance there is that a change to the application which affects one of the object's properties will affect one of the properties that we are using to identify the object. So short names (i.e., names with as few properties as possible), are more robust in the face of application changes. On the other hand, if we use too few properties we might end up with names that are match two or more objects, and if Squish cannot uniquely identify an object, how can it know which object is intended to be accessed in a test script?
There is no one right or perfect answer to this problem. Names which are invalidated because of application changes (i.e., names where the value of one or more of the properties used in the name have changed), result in test scripts that fail. Similarly, names which are no longer unique (perhaps due to using too few properties for an object and then having an application change that leads to another object with identical properties being added to the application), also leads to test script failure. So Squish must find the right balance when creating names so that they are both unique and robust.
Squish uses a set of built-in heuristics ("rules of thumb") to determine what properties to use. In most everyday testing situations these work fine and Squish creates names that are both unique and robust.
Unfortunately, there are some situations where the heuristics Squish
uses produce poor results. For example, when writing a Qt test, Squish
will use the QObject objectName property
(which Squish calls name) to identify an object,
providing this property has some text in it. In most cases this works
very well, especially if the AUT's developers have chosen unique names
for their QObjects. However, if the developers are using
this property for some other purpose so that it ends up having different
values on each run, then clearly Squish cannot rely on it, and we need
to have some way of excluding it from the list of properties that
Squish makes use of.
Starting with Squish 3.2, the property list used to create real names can be configured by editing some straightforward XML files. How to use these files to customize the name creation process is explained in the following two sections.
![]() | Note |
|---|---|
The following sections do not apply to the testing of Web applications. For these, see How to Use the JavaScript Extension API (Section 15.8). |
The list of properties that Squish uses to create multi-property (real) names for objects are specified using XML files. For each wrapper which your application uses there can be up to two XML files—called “descriptor files” in Squish terminology—which must follow a particular naming scheme so that Squish can find them:
<Squish_Directory>/etc/<wrapper>_descriptors.xml
This XML file contains the default properties which
Squish uses for creating real (multi-property) names. Depending on the
type of package you use, you might find
qtwrapper_descriptors.xml,
swtwrapper_descriptors.xml or others in your
Squish installation's etc
subdirectory.
<Squish_Directory> stands for the directory where you installed your Squish package.
<Squish_User_Settings>/<wrapper>_user_descriptors.xml
If you want to override the default behavior, or add new properties, you
can do so by creating a user descriptors file for each wrapper you want
to affect. These files have names similar to the predefined descriptor
files supplied with Squish, but with _user_
inserted in the middle of the name as shown above.
<Squish_User_Settings> stands for the directory
where user specific settings are stored. On Windows, this is the
directory %APPDATA%\froglogic\Squish, and on
Unix-like systems (Linux, Mac OS X, etc.), it is ~/.squish. If you set the environment
variable SQUISH_USER_SETTINGS_DIR to point to a different
directory, that directory is used instead for storing the user
settings—and also for descriptor files. (See Environment Variables (Section 16.6).)
Exactly the same file format is used for the predefined descriptor files that are supplied with Squish and for the user descriptor files that you can create to customize how Squish generates names.
Every descriptor file contains a list of types together with the
names of the properties that can be used when generating names for
objects of each particular type. Squish reads its own predefined
descriptor files first, and then it reads any user descriptor
files. This makes it possible for user descriptor files (those with
filenames of the form
<wrapper>_user_descriptors.xml),
to override the behavior specified in the predefined descriptor files,
and to add new type descriptors.
The list of types and the properties that can be used for their objects are specified using a simple XML format. Here's a short example for a fictional application toolkit which has a Button type:
<objectdescriptors>
<descriptor>
<type name="Button"/>
<realidentifiers>
<property>caption</property>
</realidentifiers>
</descriptor>
</objectdescriptors>
This descriptor file defines just one descriptor which says that for all objects of type Button, the 'caption' property should be used when generating the real name.
![]() | Note |
|---|---|
This means that not only objects which are instances of the Button class will get the caption property in their real name, but also instances of classes which inherit Button! When deciding which descriptors to use for generating the name for a given object, Squish takes the inheritance hierarchy into account and will use all descriptors whose type name shows up in the inheritance hierarchy of the given object. |
In this example, only the name of the type was used for identifying the object. However, it is also possible to apply constraints, so that only those objects of the given type and that meet specified constraints are identified. Here's a slightly longer example to illustrate this:
<objectdescriptors>
<descriptor>
<type name="Button">
<constraint name="visible">false</constraint>
</type>
<realidentifiers>
<property>caption</property>
<property>tooltip</property>
</realidentifiers>
</descriptor>
<descriptor>
<type name="Button"/>
<realidentifiers>
<property>caption</property>
<property>xpos</property>
<property>ypos</property>
</realidentifiers>
</descriptor>
</objectdescriptors>
Here we have created two separate descriptors, although they both refer to objects of the same type.
The first descriptor applies to objects of type Button—but only
when the button's 'visible' property is false, in other
words this descriptor only applies to hidden Buttons. So when the
application has a hidden button this descriptor says that the properties
that should be used to identify it when creating real names are the
'caption' and 'tooltip' properties.
The second descriptor applies to objects of type Button—but only visible ones since hidden Buttons are handled by the first descriptor. This descriptor says that when creating real names for Button objects their 'caption', 'xpos', and 'ypos' properties should be used.
Although we have only shown the use of a single constraint, it is possible to use as many as we like. In such cases the descriptor will only be used if all the constraints are met.
In general, when multiple descriptors are specified which apply to the same type, Squish will try to use the one that is the best match for the object it is accessing, essentially working from the descriptor with the most constraints to the one with the least.
So in terms of the second example above, if Squish encounters a Button, it will first try the first descriptor (since that has the most, i.e., one, constraints). If the button is hidden Squish has a match and will generate a real name that uses the Button's 'caption' and 'tooltip' properties. However, if the Button is visible, the first descriptor won't match, so Squish will try the next one, and this matches (since it has no constraints, so will match any Button), and generates a real name that uses the Button's 'caption', 'xpos', and 'ypos' properties.
In addition to the normal descriptors, which match an object by
the type name (and optionally, by constraints), there's a special
descriptor called * (star or asterisk), which matches
objects of any type. This special descriptor can
also have constraints applied to it, in exactly the same way as for
normal descriptors.
Here's an example of how it might be used:
<objectdescriptors>
<descriptor>
<type name="*"/>
<realidentifiers>
<property>id</property>
</realidentifiers>
</descriptor>
<descriptor>
<type name="Button"/>
<realidentifiers>
<property>caption</property>
</realidentifiers>
</descriptor>
</objectdescriptors>
In this example, a catch-all descriptor is defined. This means that for all objects, no matter what their type name is, the 'id' property will be used when generating real names. If a particular object does not have an 'id' property, the property will be silently ignored and this will not trigger an error.
![]() | Note |
|---|---|
It is harmless to list properties in a catch-all descriptor which don't exist for all object types. For example, if almost all of our object types have an 'id' property that we normally want to use when real names are generated, the best approach is to specify this property using a catch-all descriptor. This will ensure that the 'id' property is always used for real names for those objects that have it, and yet it is safely and silently ignored for those few that don't. |
When real names are generated the properties used are those for the matching descriptor, plus those for the matching descriptor of the object's parent's class, and so on, up the inheritance hierarchy. In addition, any matching catch-all descriptors are also used.
Given the example descriptors shown above, if a Button object was
encountered—and assuming that Buttons have an 'id'
property—Squish would generate a real name that would use both
the 'caption' property (from the Button descriptor)
and the 'id' property (from the *
descriptor). And if the Button type didn't have an 'id' property,
Squish would simply use the 'caption' property and ignore the 'id'
property. And if the Button type inherited another type, for example,
Widget, that had a descriptor that specified, say, a 'hasfocus'
property, then that property would also be included in the generated
real name.
Suppose we have a descriptor file that contains the following Button descriptor to be used to generate real names for the Button objects in our AUT:
<objectdescriptors>
<descriptor>
<type name="Button"/>
<realidentifiers>
<property>id</property>
<property>caption</property>
<property>tooltip</property>
<property>enabled</property>
</realidentifiers>
</descriptor>
</objectdescriptors>
This descriptor specifies four properties—'id', 'caption', 'tooltip' and 'enabled'—that will be used to generate the real names for Button objects. Unfortunately, in practice this might lead to less robust real names. This is because we are using too many properties, so our test scripts will be vulnerable if a Button's caption or its tooltip changes. What we really want to say is that if the 'caption' property has a value, then we should use it, but if the 'caption' is empty then we should use the 'tooltip' as a fallback.
Squish provides a means of solving this problem. The mechanism is part of the descriptor file format, and it allows us to specify two or more properties such that Squish will only use one of them—the first one which actually has a value. The mechanism is called “property groups”. Here is an example:
<objectdescriptors>
<descriptor>
<type name="Button"/>
<realidentifiers>
<property>id</property>
<group>
<property>caption</property>
<property>tooltip</property>
</group>
<property>enabled</property>
</realidentifiers>
</descriptor>
</objectdescriptors>
Here we have put the 'caption' and 'tooltip' properties in a group
together. The effect of this is to tell Squish to use the 'caption'
property if it has a value (i.e., if it isn't an empty string), and to
use the 'tooltip' property otherwise. So when Squish generates a real
name for a Button using this descriptor the name will have the 'id'
property, the 'enabled' property, and either the
'caption' or the 'tooltip' property—but never
both—plus any properties from the matching descriptors of the
classes that Button inherits from, plus any properties from any matching
catch-all * descriptor.
In addition to specifying the names and values of properties, real names can also specify references to related objects, and such references can help to uniquely identify an object of a particular type.
For example, we might have a form with several single line editors. In themselves these editors might all have the same properties with nothing to distinguish them, but in all probability they will all have labels beside them so that the user knows what they are expected to type into them. In some toolkits such labels might be identified by their position in relation to the object—left or above—and in other toolkits they are identified by their relationship to the object—for example, they are its “buddy”.
Here's an example real name for a fictional single line editor which is identified by its own properties, and also by its buddy property whose value is a reference to a related object:
{type='LineEdit' maxChars='32' allowDigits='false' buddy={type='Label' text='Last Name:'}}
Normal property values are enclosed in quotes, but when the value is a reference to a related object, quotes are not used and instead the related object's real name is used, enclosed in braces as usual for real names. (So we end up with one real name nested inside another.)
If we want to use a property that refers to another object in a
descriptor, we simply name it using the object tag instead
of the property tag, as the following example illustrates:
<objectdescriptors>
<descriptor>
<type name="LineEdit"/>
<realidentifiers>
<property>maxChars</property>
<property>allowDigits</property>
<object>buddy</object>
</realidentifiers>
</descriptor>
</objectdescriptors>
What this descriptor says is that when Squish encounters an object of type LineEdit, it should generate a real name that uses the LineEdit's 'maxChars' and 'allowDigits' properties, and also a 'buddy' property whose value is a reference to another object.
In some situations we want a particular property to be used when
generating real names for objects of almost every
type, but not for absolutely all of them. For example, we might have a
bunch of objects derived from a fictional Widget object and that has a
'dropEnabled' property. If most of the derived objects are editing
widgets the property makes sense, but it probably doesn't make sense for
a derived Button object. So we want to create a descriptor that includes
the 'dropEnabled' property for all Widgets, but not for Buttons.
Squish allows us to do this by using the exclude
property attribute as the following example shows:
<objectdescriptors>
<descriptor>
<type name="Widget"/>
<realidentifiers>
<property>dropEnabled</property>
<property>text</property>
</realidentifiers>
</descriptor>
<descriptor>
<type name="Button"/>
<realidentifiers>
<property exclude="yes">dropEnabled</property>
</realidentifiers>
</descriptor>
</objectdescriptors>
In this example, Widget is the base class of all GUI objects. The first descriptor tells Squish to use the 'dropEnabled' and 'text' properties when generating real names for Widget objects and for all objects that derive from Widget (such as LineEdit and Button). There is no descriptor for LineEdits since they are inherited from Widget and we have no properties to add or change. But for the Button we have created a descriptor so that we can stop the 'dropEnabled' property being used for Button names.
If we were to use the descriptor file above with our fictional toolkit, it would change the way that Squish creates real names. For LineEdits (assuming they are derived from Widget) Squish will create real names that have the 'dropEnabled' and 'text' properties (and of course the 'type' property since that is always included), but for Buttons, Squish will create real names that only use the 'text' and 'type' properties.