Table of Contents
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. This name could be a multi-property (real) name or a hierarchical name. Hierarchical names are still used by default when testing some toolkits, such as Web and Tk. For most toolkits, the default naming scheme is multi-property (real) names since they lead to much more flexible and reliable object identification in the face of application changes.
As discussed in the Object Map (Section 7.11) chapter, the representation
of real names differs depending on the kind of object map implementation used;
for Text-Based Object Map (Section 7.11.5), a real name consists 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 be a string like this, in all script languages:
{type='Button' text='Hello'}
The section Structure of Script-Based Object Maps (Section 7.11.4.2) explains that the very same object name can also be expressed as a native dictionary or map (the syntax of course varies slightly depending on the scripting language used):
{"type": "Button", "text": "Hello"}
{type: 'Button', text: 'Hello'};
{:type => 'Button', :text => 'Hello'}
{'type' => 'Button', 'text' => 'Hello'}
ObjectName type Button text Hello
Every multi-property name must have at least two properties, one of which is a mandatory toolkit-specific property. The toolkit-specific properties are:
Java: type or basetype (but not both)
Web: tagName
All others: type
Some objects can only be found inside other containing objects because of
their class design. In those cases, a container property
is also required in the real name. This is an object reference
to a container (or window, or
parentItem). Container properties are needed whenever
Squish needs to cross otherwise unrelated object hierarchies to find an object.
Some examples are: List, Tree and Table items,
QGraphicsItem
s, QtQuick Items, and HTML elements from an
embedded HTML control, such as a QWebView
.
Note that the type, basetype, tagName properties (and a few others) can only be matched exactly—most other string 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 7.10).)
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 too general, that 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 lead 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 QObject
s. However, if the property changes over
time, then clearly Squish cannot rely on it for identification purposes, and
we need to have some way of excluding it from the list of properties that
Squish makes use of.
Table of Contents
In Squish, 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.
![]() | Applies to All GUI Toolkits Except Web |
---|---|
The following sections do not apply to the testing of Web applications. For these, see Name Generation Algorithm used by Squish for Web (Section 7.12.4). |
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:
<SQUISHDIR>/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
,
javawrapper_descriptors.xml
or others in
SQUISHDIR/etc
.
<SQUISHDIR> 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 %APPDATA%\froglogic\Squish
, and on
Unix-like systems (Linux, macOS, 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 user descriptor files.
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
, caption
should be used when generating the real name.
![]() | Name Inheritance |
---|---|
This means that not only objects which are instances
of the |
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
caption and tooltip.
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.
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.
Table of Contents
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.
The toolkit wrappers that support *
as a catch-all are Qt, Java,
and Mac. For Windows or Android, use WinGUIObject
or
Control
for the type name instead.
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, id will be used when generating real names. If a particular object does not have an id, the property will be silently ignored and this will not trigger an error.
![]() | Harmless Catch-Alls |
---|---|
It is harmless to list properties in a catch-all descriptor which don't exist for some object types. For example, if almost all of our object types have an id 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 id 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 class descriptor, plus those for the matching descriptor of the object's base class, and so on, up the inheritance hierarchy. In addition, any 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, Squish would generate a real name that would use
caption (from the Button
descriptor)
and id (from the *
descriptor). And if the Button
didn't have an
id, Squish would simply use caption
and ignore id. If the Button
derived
from another type, for example, Widget
, that had a
descriptor that specified, say, hasfocus, then that
property would also be included in the generated real name.
Suppose we have a descriptor file that contains the following rule for
Button
:
<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 caption has a value, then we should use it,
but if caption is empty then we should use
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 caption and tooltip
in a <group> together. The effect of this is to tell Squish
to use caption if it has a value (i.e., if it isn't an
empty string), and to use tooltip otherwise. So when
Squish generates a real name for a Button
using this
descriptor, the name will have id,
enabled, and either
caption or
tooltip—but never both—plus any properties from
the matching descriptors of the classes that Button
inherits from, plus any properties from any 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 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. (So we end up with one real name nested inside another.)
The same object name can of course also be expressed when using Script-Based Object Map (Section 7.11.4):
{"type": "LineEdit", "maxChars": 32, "allowDigits": False, "buddy": {"type": "Label", "text": "Last Name:"}}
{type: 'LineEdit', maxChars: 32, allowDigits: false, buddy: {type: 'Label', text: 'Last Name:'}}
{:type => 'LineEdit', :maxChars => 32, :allowDigits => false, :buddy => {:type => 'Label', :text => 'Last Name:'}}
{'type' => 'LineEdit', 'maxChars' => 32, 'allowDigits' => 'false', 'buddy' => {'type' => 'Label', 'text' => 'Last Name:'}}
ObjectName type LineEdit maxChars 32 allowDigits false buddy [ObjectName type Label text {Last Name:}]
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 a
LineEdit
, it should generate a real name that includes
maxChars and allowDigits, and also
buddy which is an object reference.
In some situations we want a particular property to be used when
generating real names for most objects of a type, but not for absolutely all
of them. For example, we might have a bunch of objects derived from
Widget
with a dropEnabled.
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 dropEnabled for all
Widget
s, except for Button
.
Squish allows us to do this by using the exclude
attribute of <property> 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
dropEnabled and text when generating
real names for Widget
objects and for all objects that
derive from it (such as LineEdit
and
Button
). There is no
<descriptor> for LineEdit
since
they are derived from Widget
and we have no properties
to add or change. But for Button
we have created a
<descriptor> so that we can stop
dropEnabled from 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
LineEdit
s (assuming they are derived from
Widget
) Squish will create real names
that have dropEnabled, text (and of course
type since that is always included), but
for Button
s, Squish will create real names that only
use text and type.
Squish for Web has a different heuristic to generate object names,
it does not use the descriptor files mentioned in the previous section.
The name generation first checks all extensions that registered a hook for name
generation through Squish.nameOf
. Afterwards it applies
the following rules, taking the first one that matches. All multi-property names include
the tagName, this one is required for Squish for Web
object names. The names can also include a context
containing the name of the frame/iframe element in which the object is located.
This property is not included for objects in the toplevel page.
SELECT
, INPUT
and
BUTTON
elements include the properties
name, id,
type and innerText if they are not
empty. In addition a form property is generated
if the containing form has a name or
id property set. SELECT
and
INPUT
fields also get the type property
added to the object name.
TD
elements having the
cMenuTD
class include the properties
class, id, name
and innerText if they are not empty.
IMG
elements having id,
name or alt set to a non-empty string
will generate a multi property name including those properties. Otherwise Squish
uses a hierarchical name for IMG
elements. The
alt attribute of the element will be reflected
as img_alt in the multi-property name
The next step is walking up the hierarchy of elements to the one
for which a name is to be generated. For each parent, Squish checks if it is a
link
element. If it is, the following rules are applied to
generate a name for it:
If the element has a non-empty id, name or innerText property it will get a multi-property name including those properties.
If all of the three are empty or
not present Squish looks at the content of the link to see if there is an IMG element inside. If there
is such an element and it has a non-empty id,
name or alt attribute the name for
the link
element will use those values as img_id,
img_name and img_alt respectively
for its multi-property name.
If the inner IMG
element does not have a non-empty
id, name or
alt attribute, Squish checks the
src attribute. If that attribute is not-empty, Squish
extracts the substring after the last '/' from
the src attribute and includes that using the
img_src property in the multi-property name for
the link.
If all of the above mentioned attributes are empty Squish falls back to
using a hierarchical name for the link
element.
If the top-level element is reached Squish returns to the original object for which a name is to be generated and continues with the next rule.
If the element has a tagName and either a title, id or name attribute set, Squish generates a multi-property name including those properties.
If the element is a SPAN
,
DIV
, LI
, TD
or
TR
element and it has a non-empty
innerText attribute, then Squish generates a multi-property
name using that attribute as a property.
In all other cases Squish will generate a hierarchical name for the object
The last step is to calculate the occurrence property in case a multi-property name has been generated.
Squish for Web also allows customization of name generation. For details on that see JavaScript Extension API (Section 6.10.44).