Locator identification strategy in selenium

Choosing the right locator is very important aspect of UI test automation using Selenium. A good locator is more reliable and requires less test maintenance cost. While there are multiple ways of choosing a locator, I usually go with the below order.

We will be using this HTML page for locator identification. The HTML for the same page is shared below.

<html>

<head>
  <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css">
  <title>Login Page</title>
</head>

<body>
  <div id="content" class="container">
    <h1>Login Page</h1>
    <form id="loginForm" method="post" action="/authenticate">
      <div class="form-group">
        <label for="email" class="col-form-label">Email: </label>
        <input id="email" name="email" class="form-control" />
        <label for="password" class="col-form-label">Password: </label>
        <input id="password" name="password" type="password" class="form-control" />
      </div>

      <div class="form-group row">
        <div class="col-sm-1">
          <button type="submit" class="btn btn-primary">Login</button>
        </div>
        <div class="col-sm-1">
          <button type="submit" class="btn btn-primary">Signup</button>
        </div>
        <div class="col-sm-1">
          <button type="submit" class="btn btn-default">Forgot password?</button>
        </div>

      </div>
      <div class="form-group row">
        <div class="col-sm-4">
          <label for="language" class="col-form-label">Select Language</label>
          <select class="custom-select" name="language" id="language">
            <option value="english">English</option>
            <option value="spanish">Spanish</option>
          </select>
        </div>
      </div>
      <a href="https://www.dummy.com">Need help?</a>
    </form>
  </div>
</body>

</html>

Id

It is best to locate the element using its ‘id’ attribute. As a W3C standard , ‘id’ must be unique in the entire HTML code of a page . Sometimes ids are common (multiple elements with same id), dynamic or absent, we should look for other locators. In instances where there is no ‘id’ for the locating element, we need to look for other locating mechanism.

Example:

WebDriver driver = new FirefoxDriver();

// will get the "input" element with id="password"
WebElement password = driver.findElement(By.id("password"));

/* will get the "div" element with id="content"
 * which has the entire content within the body tag */
WebElement content = driver.findElement(By.id("content"));

CSS Selector

In CSS, selectors are patterns used to select the element(s) one wants to style. We can use the CSS selector pattern to identify one or more elements that match it. CSS selectors are very powerful because you can combine the element tags, attributes and hierarchy to uniquely identify one or multiple web elements as per the use case. Always prefer CSS over XPATH because locating elements by CSS is relatively faster than XPATH.

Example:

WebDriver driver = new FirefoxDriver();

// will get the "input" element with id="password"
WebElement password = driver.findElement(By.cssSelector("#password"));

// will get the first button with class having "btn-primary" applied
WebElement firstPrimaryButton = driver.findElement(By.cssSelector(".btn-primary"));

// will list all the buttons with class having "btn-primary" applied,
// 'Login' and 'Signup' in this case.
List<WebElement> primaryButtons = driver.findElements(By.cssSelector(".btn-primary"));

// will get first button with class having "btn" applied, 'Submit' in this case.
WebElement loginButton = driver.findElement(By.cssSelector(".btn"));

// will get list of buttons with class having "btn" applied, 'Submit',
// 'Login' and 'Signup' in this case.
List<WebElement> buttons = driver.findElements(By.cssSelector(".btn"));

// will get the element with "label" tag that has the attribute "for" set as "email"
WebElement emailLabel = driver.findElement(By.cssSelector("label[for='email']"));

What if we want to get the “Signup” button element under the login form? Since it does not have any unique identifier (neither tag, id or class), we could use css selector to find the second matching element, as shown by example below:

// will get the button under the second div tag of second div tag under the form with id "loginForm"
// which is the 'Signup' button
WebElement signupButton =
      driver.findElement(By.cssSelector("#loginForm >* >div:nth-child(2) > button"));

As you can see the locator above is now a little complicated to comprehend and should be used as a last resort. This is not the best (and stable) way to locate an element, hence use of unique identifiers for elements is highly encouraged. As a QA, you should always encourage developers to appropriately add identifying attributes to elements which will make locators for test automation easier to maintain.

XPATH

XPATH is a query language for selecting nodes from an XML document. As HTML is also a markup language, XPATH can be used to select elements (nodes) from the HTML DOM (Document Object Model). It is similar to CSS in the way that it allows us to use element tags, attributes and hierarchy to locale web elements but it is relatively slower than CSS selector. One benefit XPATH provides over CSS selector is that it provides navigation in any direction in the DOM tree (both top-down and bottom-up) whereas CSS selector provides only top-down navigation.

Example:

WebDriver driver = new FirefoxDriver();

// will get the "input" element with id="password"
WebElement password = driver.findElement(By.xpath("//*[@id='password']"));

// will get the first button with class having "btn-primary" applied
WebElement firstPrimaryButton = driver.findElement(By.xpath("//*[contains(@class,'btn-primary')]"));

// will list all the buttons with class having "btn-primary" applied,
// 'Login' and 'Signup' in this case.
List<WebElement> primaryButtons = driver.findElements(By.xpath("//*[contains(@class,'btn-primary')]"));

// will get first button with class having "btn" applied, 'Submit' in this case.
WebElement loginButton = driver.findElement(By.xpath("//*[contains(@class,'btn')]"));

// will get list of buttons with class having "btn" applied, 'Submit',
// 'Login' and 'Signup' in this case.
List<WebElement> buttons = driver.findElements(By.xpath("//*[contains(@class,'btn')]"));

// will get the element with "label" tag that has the attribute "for" set as "email"
WebElement emailLabel = driver.findElement(By.xpath("//label[@for='email']"));

If we want to get the “Signup” button element under the login form we could use XPATH to find the second matching element, as shown by example below:

// will get the button under the second div tag under the form with id "loginForm"
// which is the Signup button
WebElement signupButton =
      driver.findElement(By.cssSelector("//form[@id='loginForm']//div[2]/button"));

As an example of a bottom-up XPATH navigation, if we want to find the parent div elements for our primary buttons (Login and Signup), we could use the below XPATH.

// will get parent div elements that contain the button containing class 'btn-primary'
List<WebElement> primaryButtonParents =
   driver.findElements(By.xpath("//*[contains(@class,'btn-primary')]//parent::div"));

Name

Name is another way to locate the element. When locating a single element, it is not a preferred locator strategy except when the name is unique and attribute ‘id’ is not present in the locating element.

Example:

WebDriver driver = new FirefoxDriver();

// will get the "input" element with id="password"
WebElement password = driver.findElement(By.name("password"));

Class Name

This locator strategy uses class attribute to find the locator. When locating a single element, a good practice is to make sure that the class name is unique and it’s not going to be used further in the same page in near future. This locator can also be used to find the group of elements that have same class name. We cannot use this locator to fine elements using multiple classes.

Example:

WebDriver driver = new FirefoxDriver();

// will get the "button" element with class="btn-default"
WebElement button = driver.findElement(By.className("btn-default"));

//This is not going to work as ClassName is used to find only single class.CSS selector is the right locating strategy in this case.
WebElement button = driver.findElement(By.className("btn btn-default"));

Tag Name

TagName is a good choice if the locator element is one of the multiple choice elements (radio buttons, check box, drop down). It can also be used to locate an element through its tag.

Example:

WebDriver driver = new FirefoxDriver();

//will get the "select" element
Select select = new Select(driver.findElement(By.tagName("select")));

// will select the item with visible text "English"
select.selectByVisibleText("Engish");

//will get the "form" element within the HTML page.
WebElement button = driver.findElement(By.tagName("form"));

LinkText

It can be used to find hyperlink on the page. In case the hyperlink text vary on different locale (the hyper link text can vary based on different regions), we must consider using other locator strategy or keep globalization and localization in mind while using linkText locator strategy.

Example:

WebDriver driver = new FirefoxDriver();
WebElement hyperlink = driver.findElement(By.linkText("Need help?"));

Partial LinkText

It works similar to LinkText with the difference that you can use partial hyperlink text to locate the element.

Example:

WebDriver driver = new FirefoxDriver();
WebElement hyperlink = driver.findElement(By.PartialLinkText("help"));

See also