One of the more challenging aspects of creating Squish tests is the generation of stable object names. The name to identify an object should ideally never match any other object on the screen. And using a name for identifying an object should not include data that is to be used for a verification, since that would disallow doing multiple verifications if one of them fails as an object lookup error aborts the test execution.
In modern Web applications it is common to use data attributes to include internal data information for web objects visualizing that data. This internal data can be useful to improve object names that are unstable or that should be isolated from expected changes in the visible text of the object.
A simple example test
I am going to use a short stripped-down addressbook table, that is loosely based on our own Addressbook example application. I have added some HTML5 data attributes (highlighted in the code snippet) to show how Squish can use these. The HTML code is rather short:
<title>Demo</title> <table border="1"> <tbody> <tr> <th style="width:10em;">Name</th> <th style="width:8em;">Phone</th> <th style="width:20em;">Email</th> </tr> <tr data-row-id="1021"> <td data-column-id="4" style="color: #444444;font-weight: bold" data-name="Aron Grieve">A. Grieve</td> <td data-column-id="7">573 879 8988</td> <td data-column-id="6">firstname.lastname@example.org</td> </tr> <tr data-row-id="1052"> <td data-column-id="4" style="color: #aaaaaa;font-weight: bold" data-name="Alanah Grieve">A. Grieve</td> <td data-column-id="7">591 326 1378</td> <td data-column-id="6">email@example.com</td> </tr> <tr data-row-id="921"> <td data-column-id="4" style="color: #777777;font-weight: bold" data-name="Bella Atkinson">B. Atkinson</td> <td data-column-id="7">550 500 3059</td> <td data-column-id="6">firstname.lastname@example.org</td> </tr> </tbody> </table>
In order to access this from a browser with Squish it can be placed into the examples/web/addressbook directory and then starting the server.py script in the same directory as outlined in the Using the examples note in the S/Web tutorial.
Here is a short test script that verifies the CSS font weight and color (accessible through the style() function) of the first cells in the first column. It then goes on and verifies the visible content of the rest of the cells of these two rows against expected values:
def main(): startBrowser("http://localhost:9090/data_attribute_scenario.html") test.compare(waitForObjectExists(":Demo.A. Grieve_TD").style().value("fontWeight"), "bold") test.compare(waitForObjectExists(":Demo.A. Grieve_TD").style().value("color"), "#444444") test.compare(waitForObjectExists(":Demo.A. Grieve_TD_2").style().value("fontWeight"), "bold") test.compare(waitForObjectExists(":Demo.A. Grieve_TD_2").style().value("color"), "#aaaaaa") test.compare(waitForObjectExists(":Demo.573 879 8988_TD").simplifiedInnerText, "573 879 8988") test.compare(waitForObjectExists(":Demo.591 326 1378_TD").simplifiedInnerText, "591 326 1378") test.compare(waitForObjectExists(":Demo.email@example.com_TD").simplifiedInnerText, "firstname.lastname@example.org") test.compare(waitForObjectExists(":Demo.email@example.com_TD").simplifiedInnerText, "firstname.lastname@example.org")
The first notable thing in this test script are the two names that Squish generated for first two cells in the Name column, Demo.A. Grieve_TD and Demo.A. Grieve_TD_2. The symbolic names are quite similar, which reflects that the real names are similar too. We can look at the real names by right-clicking the symbolic name text and choosing Open Symbolic Name. The only difference between these two is the occurrence property, since both have the same text value.
There is no other identifying property that Squish can use by itself, but the occurrence property can become a problem. If a third entry appears that also has ‘A. Grieve’ as abbreviated name it may end up being before the other two. Now the object name with an occurrence of 2 will identify the object having the lightest gray color and the verification will fail. The same problem appears when the order of these two entries changes.
Another problem with this test and the generated names are other cells of the table. They are all identified using their text content in the simplifiedInnerText property. So the name carries the data that the verifications want to check. This is not a big deal as long as the test passes, however once the first phone number changes, the lookup of the name will fail and this will terminate the whole test case execution.
A more stable set of object names
It is possible to avoid the outlined issues by leveraging the data attributes that have been added to the HTML code.
The names :Demo.A. Grieve_TD and :Demo.A. Grieve_TD_2 can be modified to include the data-name attribute instead of using simplifiedInnerText. The data-name includes the full name of the person and thus we can remove the occurrence property from the second name. The following screencast shows the modification steps I outlined in more detail:
As you can see at the end the symbolic names are being modified so they are more self-explanatory when being used in the test script. The corresponding usage of these symbolic names in the test script now has to be adapted too:
test.compare(waitForObjectExists(":Demo.Aron Grieve_TD").style().value("fontWeight"), "bold") test.compare(waitForObjectExists(":Demo.Aron Grieve_TD").style().value("color"), "#444444") test.compare(waitForObjectExists(":Demo.Alanah Grieve_TD").style().value("fontWeight"), "bold") test.compare(waitForObjectExists(":Demo.Alanah Grieve_TD").style().value("color"), "#aaaaaa")
The second problem – the existing names for the cells currently carry the text that is to be verified – can be avoided in a similar way: The HTML code includes an identifier for the column data-column-id that is independent of the actual text. Just replacing simplifiedInnerText with data-column-id however presents a new problem: There are now two object names that have the same set of properties and will identify the same cell. The data-column-id property itself is not sufficient to address a particular cell. It is necessary to also ensure the column is looked up in the right row of the table. Luckily, Squish for Web allows to use any HTML object name as a container for other object names and thus limit the lookup to that container object. So it should be sufficient to create an object name for the two rows and use that as a container. The table rows all carry a row identifier as data-row-id that makes it a perfect candidate for an identifying property. The individual steps I took for this modification can be seen in the following screencast:
Modifying the hierarchy of objects or property values does not require any change in the script code. Now the test will not abort on the first changed phone number with an exception anymore, instead just a test failure is being logged and the next cell is verified.
Further Development ideas
There are some things that could be improved in these short examples already, some ideas are:
- The symbolic names of the objects, particularly the phone number and email cells could be adjusted to not carry the text of the cells to avoid any confusions when the phone number is intentionally changed.
- The verifications of the cells could be changed to be data-driven and build object names on the fly, so the object map does not explode when testing a larger dataset.
- the verification of the table cell contents should likely also include the first column for completeness.
- With Squish 6.3 it is possible to augment the Squish for Web name-generation to include data-properties automatically when generating names.