Selenium is not brittle, you’re just using it wrong!

They say that the most important personal achievements are motivated by either strong love for someone or something, or strong hate. If that’s true, then this should be a good article, because I absolutely hate the phrase “Selenium is brittle”. I wouldn’t blame you if you find yourself in agreement with it, but I’d say there’s a very strong possibility that you’re blindly swallowing what the evangelists of the “new world order” are serving you. Selenium is nothing more that a tool, capable of producing wonderful results when used right, or disappointing ones when handled improperly.

Exhibit A: clicking an element with Selenium

How do you properly click an element with Selenium? And no, it’s not driver.findElement().click(). Since test automation is, after all, a software development activity, let’s do what good software engineers do and play a game of “what if”.

What if the element we’re trying to click doesn’t exist just yet?

This should be an easy one, which I bet most testers are already familiar with. The advent of the single-page web applications (SPAs) and asynchronous JavaScript means that a lot of the elements in a Web page nowadays are created dynamically in code, which means that you must wait for the element to be created before clicking it. So let’s wait for 10 seconds for our element to “arrive”:

WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(
ExpectedConditions.presenceOfElementLocated(locator));

Once we’ve established that the element exists, we can go ahead and click it, right? Not quite.

What if the element is disabled or not visible?

Now that we know that the element exists, we have to ensure we’re not dealing with something like a disabled button or a hidden element (with its CSS set to display: none). Here’s how we do that:

wait.until(ExpectedConditions.elementToBeClickable(element));

Well, what about now? Can we finally click this dang element? Nope. Not yet.

What if our element is covered by something else?

Unfortunately, calling the ExpectedConditions.elementToBeClickable method is still not enough to absolutely guarantee that we can perform the click, since our element might be temporarily covered by some other element (think progress indicator, lightbox dialog, animation, etc.). This means we have to be ready for our click to fail and perform some retry logic. We can do that by writing a custom condition named CustomConditions.elementWasClicked, similar to what Selenium provides out-of-the-box:

public class CustomConditions {
public static ExpectedCondition<Boolean> elementWasClicked(
final WebElement element) {
return new ExpectedCondition<Boolean>() {
@Override
public Boolean apply(WebDriver driver) {
try {
element.click();
return true;
} catch (Exception ex) {
return false;
}
}
@Override
public String toString() {
return "element to be clicked: " +
element.toString();
}
};
}
}

Now, we can finally click our element with a reasonable degree of confidence, like this:

WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement element = wait.until(
ExpectedConditions.presenceOfElementLocated(locator));
wait.until(ExpectedConditions.elementToBeClickable(element));
wait.until(CustomConditions.elementWasClicked(element));

And that’s how you properly click a Web UI element. You can encapsulate that logic into your own click method and use it whenever you need to click an element. The astute reader will notice that, for all the precautions we took, we are still not handling the scenario where the element is not in the Web page’s viewport and we must scroll to it first, but this scenario is not as common with web applications and we’ll leave it as a subject for a future post.

Conclusion

One way to protect yourself against the type of problems explored in this article is to make sure you ask the right “what ifs”, incorporate them as classes or methods into your test framework as reuse them across your entire test suite.

While writing stable Selenium tests requires some level of craft and care, I would argue that Selenium, as a browser automation tool, does exactly what it was designed to do and does it well. After all people don’t blame the tool factory if they happen to hit their finger with a hammer, do they? Please don’t let anybody say “Selenium is brittle” ever again.

The code presented in the article is adapted from the OpenTest functional test automation tool for web, mobile and APIs. OpenTest is a free and open source project designed to hide the complexity involved in automated testing and allow test engineers to focus on writing tests that make sense, rather than building test frameworks.

If you liked the article, please click and hold the clap button, so others can find it too.

Passionate software engineer, proud parent of two, author of the OpenTest functional testing tool for web, mobile and APIs.