Protractor and Selenium are widely used tools for building functional end-to-end (e2e) automated tests. These tests execute a web-based application under test through a browser interface. The automated tests need to examine web page elements to determine whether their contents match expected results. They also need to operate on a webpage’s elements to drive the application. The tests need to click buttons, select from menus, and enter text in fields as part of the automated tests. A key problem and design decision is how the automated tests will locate elements in the DOM.
Selenium and Protractor, which is built on Selenium’s WebDriver, have a substantial set of functions for finding and retrieving elements in a DOM. There are built-in methods to retrieve elements by tag, id, name, class, and text.
For example, using Protractor, we might write:
this._filterElement = element(by.id(‘customFilter‘));
to make a web page element with the Id attribute value of “customFilter” accessible to the test.
WebDriver also has methods for retrieving using XPath and CSS expressions. For example:
this._appsMenu = element(by.xpath(“.//*[@layout-scheme=’menu’]”));
this._gpsApp = element(by.css(“.menuIcon.perf2Icon”));
uses an XPath expression and a CSS expression to retrieve a button and an icon.
In most applications, the class and id attributes are already being used for applying CSS or for navigation within a page. These uses make class and id difficult to also use for locating elements in the DOM.
Usually a team building automated tests is working closely with the development team. In this situation the development team can make code changes to make the job of the automated test team easier.
One way to identify elements is to add an attribute that will be used for nothing except the automated tests. For instance, we might add an attribute xx-test-id to each element that the automated test needs to access, where xx could be replaced by a company name or acronym. We assign a unique value to each different occurrence of the xx-test-id. The test team should keep a list of attribute values to ensure that values are unique.
The code above would be written using this technique as:
this._filterElement = element(by.xpath(“.//*[@ab-test-id=’customFilter‘]”);
this._appsMenu = element(by.xpath(“.//*[@ab-test-id=’buttonApps’]”));
this._gpsApp = element(by.xpath(“.//*[@ab-test-id=’gpsApp’]”));
One great advantage of using this approach is the uniformity of the code in the automated tests. Instead of customizing a call for every element that the test needs to access, we use one consistent method to access elements. There is some small performance penalty for using XPath for element location instead of using WebDriver’s built in by.id() and by.className() functions, but the advantages outweigh this concern.