How to find user interface elements using locators in web applications

A "simple" request

  • 🙋‍♀ïļ Hey, Robot! Click that button for me, will you?
  • ðŸĪ– >>> print("PARDON ME?")
  • 🙎‍♀ïļ That button! Right there!
  • ðŸĪ– ERROR: "That button! Right there!" NOT FOUND

What to click, that is the question

Here we have three buttons. To click the (That button!) button, you shout an order at your robot, and it will complete this trivial task, right? As it turns out, it's not quite that simple.

What's under the hood?

To understand what the robot sees, let's take a look at how the above buttons are built. You can use a desktop browser, such as Firefox or Chrome, or some other browser, as long as it comes with developer tools. Those tools provide developers with great powers, such as inspecting the HTML elements on a web page.

Right-click on the That button! button and select Inspect Element (Firefox) or Inspect (Chrome). An Inspector view opens. Moving your mouse cursor over the HTML markup in the inspector highlights the corresponding elements in the browser window.

Inspect element using browser developer tools

Here is the HTML markup of the buttons:

<span id="locator-example">
  <button name="that-button" style="color: black; padding: 0.5em; border: solid 1px;"><i>That</i> button!</button>
  <button id="sad-button" name="sad-button" style="color: black; padding: 0.5em; border: solid 1px;">
    Sad button ðŸ˜Ē
  </button>
  <button class="mad-button" style="color: black; padding: 0.5em; border: solid 1px;">Mad button ðŸĪŠ</button>
</span>

Grabbing the CSS locator

To click the button, the robot needs to locate it first. To grab the locator, right-click on the <button name="that-button"... element in the inspector. Select Copy -> CSS Selector (Firefox), or Copy -> Copy selector (Chrome). The result is the following CSS selector:

#locator-example > button:nth-child(1)

Testing the CSS locator

To test the CSS selector (locator), click on the Console tab in the developer tools and run this JavaScript command:

document.querySelector('#locator-example > button:nth-child(1)');

The element matching the CSS selector is printed out in the console. Hovering the mouse cursor on the element in the console highlights it on the web browser window. This way, we can verify that the selector matches the correct element.

Testing the CSS selector in developer tools console

We could use the CSS locator in a robot script like this:

*** Keywords ***
Click That Button
    Click Button    css:#locator-example > button:nth-child(1)

The css: prefix uses the CSS locator strategy when locating the element (there are multiple available locator strategies).

Done?

Alright! Commit, push, deploy to production? The locator matches the first button element that has a parent element with an id attribute of locator-example (using the :nth-child() CSS pseudo-class).

What if someone modifies the order of the buttons? The robot would end up clicking on the wrong button since it always clicks the first button! We have to find a more robust locator that will keep working even if the buttons are shuffled around. This is the HTML markup for the button we are interested in:

<button name="that-button" style="color: black; padding: 0.5em;"><i>That</i> button!</button>

There is no way to locate the button using the button text (That button!) if we want to continue using CSS locators. Time to move to other locator strategies? Not yet.

Finding a more robust CSS locator

Locating elements by things that might change, such as their exact position or label, can cause your robot to break easily. For example, your robot breaks if someone decides to change the text on the button or to translate the text into other languages.

A good locator is one that is unlikely to break often. Good candidates for robust locators are the id and name attributes of HTML elements. An id attribute must have a unique value in the whole HTML document. Locating an element using the id attribute is thus a good option (assuming the value is static and does not change dynamically). The name attribute used with many form elements does not have to be unique but is often good enough for automation purposes. We could modify the CSS locator like this to use the value of the name attribute:

#locator-example > button[name='that-button'];

Make it as simple as possible, but no simpler

If we have only one button named that-button in our HTML document, we can further simplify the locator by omitting the #locator-example > part, making the locator less strict:

button[name="that-button"]

Our robot keyword might look like this:

*** Keywords ***
Click That Button
    Click Button    css:button[name="that-button"]

The Click Button keyword has built-in logic for locating buttons by their id, name, and value attributes. We can reduce the locator to a really short form:

*** Keywords ***
Click That Button
    Click Button    that-button

If you want to communicate that you are relying on the name attribute for locating the button, you can use the name: strategy prefix:

*** Keywords ***
Click That Button
    Click Button    name:that-button

One locator to rule them all

Automatic locator extraction works best when the element has a unique id attribute. Right-click on the <button id="sad-button"... element in the inspector. Select Copy -> CSS Selector (Firefox), or Copy -> Copy selector (Chrome). The result is the following CSS selector:

#sad-button

Whenever possible, use id attribute locators. They are typically the most robust ones and target exactly the elements you want. In this case, both Firefox and Chrome automatically chose the id as the selector (locator). We could use the CSS id locator in a robot script like this:

*** Keywords ***
Click That Button
    Click Button    sad-button

If you want to communicate that you are relying on the id attribute for locating the button, you can use the id: strategy prefix:

*** Keywords ***
Click That Button
    Click Button    id:sad-button

Who are you? - No one.

What if the element lacks both id and name attributes? If the element has CSS classes, we could use those for locating. Right-click on the <button class="mad-button"... element in the inspector. Select Copy -> CSS Selector (Firefox), or Copy -> Copy selector (Chrome). The results are the following CSS selectors:

Firefox:

.mad-button

Chrome:

#locator-example > button.mad-button;

We could use the CSS class locator in a robot script like this:

*** Keywords ***
Click That Button
    Click Button    css:.mad-button

Other locator strategies: XPath

Right-click on the <button name="that-button"... element in the inspector. Select Copy -> XPath (Firefox), or Copy -> Copy XPath (Chrome). The results are the following XPath expressions:

Firefox:

/html/body/div/div[1]/div/main/div/section/div/article/div/div/span/button[1]

Chrome:

//*[@id="locator-example"]/button[1]

Testing the XPath locator

To test the XPath expression (locator), click on the Console tab in the developer tools and run this command:

$x('//*[@id="locator-example"]/button[1]');

The $x(...) statement works in both Firefox and Chrome for executing XPath expressions. We could use the XPath locator in a robot script like this, using the xpath: locator strategy prefix:

*** Keywords ***
Click That Button
    Click Button    xpath://*[@id="locator-example"]/button[1]

Analyzing the XPath locator

Firefox provides the full XPath from the root html element to the button. This type of locator will break easily, as any HTML structure changes will cause the locator to fail. Chrome does a better job here (if we consider it better as in "more robust for our use case"). Chrome's XPath expression reads: "The first button element under a parent element that has an id attribute with the value of locator-example.". Neither of these XPath locators is as robust as the id or name attribute CSS locators.

Use cases for the XPath locator strategy

XPath is useful in cases where the CSS strategy does not work. XPath works, for example, in cases where you need to select the parent of a child element. CSS works only for cases where you select a specific element or a child element. Traversing to the parent element is not currently supported by the CSS specification.

See the Web scraper robot for an example where an XPath expression is used to find tweet elements by the text they contain (user name). The user name is inside a child element of the parent tweet element, a perfect use case for the XPath locator strategy.

Automatic vs. manual selection for locators

It is recommended to select and fine-tune locators manually. Some of the automatically detected locators might be good enough. Still, you want to use the most robust version you can to avoid your robot breaking whenever the application changes even just a bit. The automatic locator extraction works well when the element has an id attribute (assuming the value of the attribute does not change).

If the application you are automating is under your control, try to add static id and name attributes, or well-named CSS classes to your elements. These rarely change, and they avoid the issues with text labels since those might change quite often. They also assist with automated testing efforts. The easier it is to target elements in your application, the easier it is to automate.

Try the CSS locator strategy first (typically simpler and also faster to execute). If that fails, try the XPath locator strategy.