Top Tips for Selenium Experts - Part 1: Techniques
November 12, 2019

Tony Yoshicedo
Parasoft

Once you have been using Selenium for a while and are comfortable writing test cases, you can focus on techniques and design principles to get your UI test automation to the next level.


This blog assumes you have been using Selenium and are comfortable writing test cases. You've already gone through the trial of inspecting DOMs to create your own XPaths. Maybe you are using the Page Object Model. By now you are probably pretty good at looking up solutions on the internet.

This 2-part blog is broken down into techniques and design patterns. Techniques are covered here in Part 1, and design patterns will be covered in Part 2 . You may already know all about some of these. That's fine, just skip to the sections that interest you. I'll be using Java here, but the techniques and practices should be generally applicable.

Better Locators

Bad locators cause spurious test failures. They waste time and distract from the intended function of the test. Bad locators typically rely on some unstable quality of the web page, whether that's a dynamic value or the element's position on the page. When those qualities invariably change, the locator breaks. Good locators just work. They correctly identify their intended element and allow the test to do its job.

The key here is to identify the qualities of the element that are stable, then choose the minimum subset of these qualities that uniquely identify that element to reduce your exposure to change. We all know absolute XPaths are bad, but it's good to understand exactly why.

/html/body/div[25]/div[2]/div/span/span[2]/div/h2/p

Take one subsection of this XPath: div[25]/div[2]. This represents the following qualities:

■ The node is somewhere in a div
■ That div is the second div
■ That div is directly within another div
■ That div is the 25th div

In this small subsection we have at least 4 qualities used to identify an element. If any of them change, our locator breaks. We have no reason to believe they will not change because none of them actually describe the element. Looking at this XPath, we couldn't even guess the purpose of the element.

Locators that reflect the purpose of the element are less likely to break. While the position or appearance of the element might change, its function should not. Ideally, you can guess what the element does by looking at its locator.

//p[contains(@class, "content")][contains(.,"Guidance for Success")]

With the above it's clear the element represents content that describes "Guidance for Success." Notice the class attribute, while usually used to control the appearance, also describes the element's function.

Often the unique, stable qualities of an element are not found in the element itself. Instead you will need to go to some relative of the element whose function is well described in the DOM. Take the above example in the "Guidance for Success" element. While this locator is good, copy text can often change, or may not be an option if the site supports multiple languages. Searching the DOM we might find that the parent div has a descriptive id. In that case, we might prefer a locator like:

//div[@id="success_guide"]//p

Explicit Waits

The temptation here is to set an implicit wait and hope that will handle most issues. The problem is this broad approach treats all situations the same without even addressing most problems associated with waiting. What if the element becomes present after a few seconds, but is not yet ready to be clicked? What if the element is present, but is obscured by an overlay? The solution is to use well-crafted explicit waits.

Explicit wait conditions take the form of:

WebDriverWait wait = new WebDriverWait(webdriver, timeOutInSeconds)
wait.until(/* some condition is true */)

Explicit waits are powerful because they are descriptive. They allow you to state the necessary conditions in order to proceed. A common example of this is when the test needs to click an element. It is insufficient for the element to just be present; it needs to be visible and enabled. Explicit waits allow you to describe these requirements directly in the test. We can be confident the conditions have been met before the test attempts to continue, making your tests more stable:

WebDriverWait wait = new WebDriverWait(webdriver, timeOutInSeconds)
wait.until(ExpectedConditions.elementToBeClickable(element))

Descriptive explicit waits also let you write tests that focus on the behavioral aspects of the application. Because the application behavior does not change often, this allows you to create more stable tests. Given the example above, it's necessary for an element to be visible and enabled to be clicked, but it also needs to not be obscured by another element. If there is a large "loading" overlay covering your element, the click will fail. We can create an explicit wait that describes this loading behavior and waits for the necessary conditions to be met:

WebDriverWait wait = new WebDriverWait(webdriver, timeOutInSeconds)
wait.until(ExpectedConditions.invisibilityOf(loadingOverlay))

ExpectedConditions

You may have noticed the ExpectedConditions utility methods being used in the above examples. This class contains a large number of helpful conditions to be used when writing your Selenium tests. If you haven't already, it's worth taking a moment to go over the full API. You will commonly use

ExpectedConditions.elementToBeClickable(..) or ExpectedConditions.invisibilityOf(..), but you may also find uses for alertIsPresent(), jsReturnsValue(...), or titleContains(..). You can even chain the conditions together using ExpectedConditions.and(..) or ExpectedConditions.or(..).

Executing JavaScript

WebDrivers provide the ability to execute JavaScript within the context of the browser. This is a simple feature with incredible versatility. This can be used for common tasks, such as forcing a page to scroll to an element, as with:

driver.executeScript("arguments[0].scrollIntoView(false)", element)

It can also be used to leverage the JavaScript libraries used in an application such as JQuery or React. For example, you could check if the text in a rich editor had been changed by calling:

driver.executeScript("return EDITOR.instances.editor.checkDirty()")

The executeScript feature opens up the entire library API to your test. These APIs often provide useful insight into the state of the application that would otherwise be impossible or unstable to query using WebElements.

Using library APIs does couple your test to a library implementation. Libraries can often be swapped during the development of an application, so caution is required when using executeScript this way. You should consider abstracting these library-specific calls behind a more abstract interface to help reduce your tests' exposure to instability, such as with the Bot pattern (see more info in Part 2 of this blog).

Read Top Tips for Selenium Experts - Part 2: Design Patterns

Tony Yoshicedo is a Senior Software Developer at Parasoft
Share this

Industry News

June 01, 2020

IT Revolution announced a full conference agenda for DevOps Enterprise Summit London, June 23-25, 2020.

June 01, 2020

Caltech CTME announced that Simplilearn, a global provider of digital skills training, will collaborate with CTME (Caltech's Center for Technology and Management Education) to offer a specialized Post Graduate Program in DevOps software engineering.

June 01, 2020

DevOps Institute, a global member-based association for advancing the human elements of DevOps, announced the introduction of its SKILup Playbook Library, a dynamic collective body of knowledge (cBok) that aligns thought leadership from industry experts with a set of dynamic, orchestrated artifacts, research and assets.

May 28, 2020

Docker has extended its strategic collaboration with Microsoft to simplify code to cloud application development for developers and development teams by more closely integrating with Azure Container Instances (ACI).

May 28, 2020

Eggplant announced updates to its Digital Automation Intelligence (DAI) platform.

May 28, 2020

Aptum launched its Managed DevOps Service in partnership with CloudOps, a cloud consulting and professional services company specializing in DevOps.

May 27, 2020

Red Hat announced an expansion of its application services portfolio with the addition of Quarkus as a fully supported framework in Red Hat Runtimes.

May 27, 2020

Couchbase has completed a $105 million all-equity Series G round of fundraising.

May 27, 2020

Aqua Security closed a Series D round of $30M led by Greenspring Associates.

May 26, 2020

GitLab is releasing 13.0 of its DevSecOps platform to enable organizations to efficiently adapt and respond to new and dynamic business challenges.

May 26, 2020

Solo.io announced the availability of the Istio Developer Portal to streamline the developer onboarding process for improved developer experience and increased productivity with added security features.

May 26, 2020

WhiteHat Security will offer free application scanning services to any education institution to support secure online learning.

May 21, 2020

Exadel announced the Grand Prize winner of the “Appery.io COVID-19 Virtual Hackathon.”

May 21, 2020

CloudBees announced significant advances for its Software Delivery Management (SDM) platform – integrations with additional continuous integration and continuous delivery (CI/CD) engines, including Google Cloud Build and Tekton, and extension of the availability of CloudBees’ SDM Preview Program.

May 21, 2020

OutSystems is announcing over 70 development accelerators that ensure web and mobile applications created on the OutSystems low-code development platform can comply with the highest accessibility standards and regulations.