7.11. Object Name Generation

7.11.1. Object Names
7.11.2. Defining Property Sets
7.11.3. Advanced Property Set Definitions
7.11.4. Name Generation Algorithm used by Squish for Web

7.11.1. Object Names

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

7.11.2. Defining Property Sets

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]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.11.4).

7.11.2.1. Descriptor File Locations

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

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.

7.11.2.2. Descriptor File Format

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]Name Inheritance

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.

7.11.3. Advanced Property Set Definitions

7.11.3.1. The Catch-All * Descriptor

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]Harmless Catch-Alls

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.

7.11.3.2. Groups of Exclusive Properties

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.

7.11.3.3. Properties with Object Name Values

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.

7.11.3.4. Excluding Individual Properties From Some Objects' Real Names

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.

7.11.4. Name Generation Algorithm used by Squish for Web

Squish for Web has a custom heuristic implemented to generate object names, it does not use the descriptor files explained in the above paragraphs. 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 property, this one is required for Squish for Web object names. The names can also include a 'context' property 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 the 'id', 'name' or 'alt' property 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' property 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' name 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 to customize the name generation, for details on that see JavaScript Extension API (Section 6.10.42).