logo logo

Use Smart Selenium Waits (FluentWait) to Avoid Flakiness

main post image

Waits in automated testing are crucial and in any project we have – we will need to include some kind of waiting mechanism in order to maintain the stability of our tests and avoid flakiness. What does that mean? We will want to synchronize between our testing framework/driver/tool (for example, Selenium) and between our Application Under Test (the website).

This is important because sometimes our software can behave differently than usual with timing issues, due to web site latency caused by working with a slow internet connection, or a variety of other reasons…

It’s important to differentiate between a system that is slow for a reason on which we will open a bug, as opposed to a system that is slow not by our control. In this article, I will talk about the latter, since it won’t be reasonable to open a bug on a system working slow only due to one of the internet providers crashing, right? 🤔

In case we won’t make sure to synchronize between the systems, our tests will simply fail when the connection is not stable. We should always strive to avoid such issues by adding waiting mechanisms to our code.

Selenium Smart Waits – Let’s Get Up and Close with FluentWait

Anyone who has been playing around with Selenium or has learned a bit automation, and knows how to code – is aware of some simple waits, such as: Thread.sleep and smarter waits such as: Explicit Wait, Implicit Wait, Page Load Timeout. I am NOT going to talk about these waits in this post, but rather in this post I’m going to talk about a smart generic wait named: Fluent Wait ✨

The Fluent Wait is a type of Explicit Wait. You can categorize the Explicit Wait into 2 classes in Selenium:

  1. WebDriverWait – Probably something all test automation developers are aware of.
  2. FluentWait – Probably something less familiar, but more generic. In fact, you could say that the WebDriverWait inherits from the FluentWait.

While working with FluentWait, we can define a few “rules” during the waiting time:

  1. The element we want to wait for.
  2. The upper bound – Maximum time we want to wait for the element.
  3. The frequency we’d like to search the DOM to check if the element appeared.

Before diving into the code, let’s first see an example of what we want to test. Let’s say we have such a button:

smartWaiting1

 

We can identify the button by: id=start2

smartWaiting2

 

When I click the button, this “loading” indication will appear for a few seconds:

smartWaiting3

 

Then this following text will appear, and it can be identified by: id=finish2

smartWaiting4

 

In our test, we will want to click the button and wait until this text: “My Rendered Element After Fact!” will appear on the screen. Since this text will only appear after a few seconds, if we won’t use waits, our test will fail.

The general Fluent Wait code should look like this:

Wait wait = new FluentWait(driver)
    .withTimeout(10000, TimeUnit.MILLISECONDS)
    .pollingEvery(350, TimeUnit.MILLISECONDS)
    .ignoring(NoSuchElementException.class);

WebElement element = wait.until(new Function<WebDriver, WebElement>() 
{
  public WebElement apply(WebDriver driver)
  {
     return driver.findElement(By.id("ABC123"));
  }
});

What do we have here? We can see the wait defined with parameters of: withTimeout equal to 10 seconds (this is the upper bound defined for the wait), pollingEvery equal to 350 miliseconds (the frequency the wait will search the DOM) and NoSuchElementException class from which the wait will ignore. What does that mean? 😲

First, let’s understand what happens if we wait for a certain element and it does not appear within the maximal time frame we’ve set:

  • Without using any sort of Wait – We will get an exception of: NoSuchElementException.
  • While using ImplicitWait – We will get an exception of: NoSuchElementException.
  • While using ExcplicitWait – We will get an exception of: TimeoutException.

With FluentWait, we can define the exceptions’ classes from which we will want to ignore during the waiting time. In our case, we will want to ignore the class NoSuchElementException. If we do not ignore it – The wait will not occur and we will immediately receive that exception.

Therefore, in our case, we can use the FluentWait code in our own function and call it: waitedElement and it will receive locator as a parameter – The same locator that identifies the element we want to search for.

In addition, we can define our waiting variables as global, or alternatively as external variables  (defined in the config file for example).

We can also return the element from the wait function, in order to be able to interact with it later on. For example, this is how we will define our function:

long timeout = 5000;
long interval = 500;

public WebElement waitedElement(By locator)
{
  Wait wait = new FluentWait(driver)
      .withTimeout(timeout, TimeUnit.MILLISECONDS)
      .pollingEvery(interval, TimeUnit.MILLISECONDS)
      .ignoring(NoSuchElementException.class);

  WebElement element = wait.until(new Function<WebDriver, WebElement>() 
  {
    public WebElement apply(WebDriver driver)
    {
      return driver.findElement(locator);
    }
  });
  return element;
}

And the call for the function from within the test will be:

By finish = By.id("finish2");

@Test
public void Test3_ExplicitWait() 
{ 
  driver.findElement(By.id("rendered")).click(); 	
  System.out.println(waitedElement(finish).getText());
}

Pay attention that I printed the element’s text. In an actual real test, I will want to add it to the assert.

I can also play a bit with the function and define it so that instead of returning the element, it will perform an action on it (and later on I can also report to the log or reports…)

long timeout = 5000;
long interval = 500;

public void waitForElementAndClick(By locator)
{
  Wait wait = new FluentWait(driver)
      .withTimeout(timeout, TimeUnit.MILLISECONDS)
      .pollingEvery(interval, TimeUnit.MILLISECONDS)
      .ignoring(NoSuchElementException.class);

  WebElement element = wait.until(new Function<WebDriver, WebElement>() 
  {
    public WebElement apply(WebDriver driver)
    {
      return driver.findElement(locator);
    }
  });
  element.click();
}

What are you waiting for? 😉

Go ahead and implement such waiting mechanisms within your own tests in order to maintain the stability of your automated tests as much as possible. Happy Testing! 💪

Reference: http://atidcollege.co.il/smart-wait/

 

Yoni Flenner

About the author

Yoni Flenner

A software engineer who likes to test things programmatically, and he’s doing it for a living for the past 10 years: http://atidcollege.co.il/

Join TestProject Community

Get full access to the world's first cloud-based, open source friendly testing community. Enjoy TestProject's end-to-end test automation Platform, Forum, Blog and Docs - All for FREE.

Join Us Now  

Leave a Reply

Join TestProject Newsletter

Join a 20K community of readers! Always stay up-to-date with all the latest test automation trends, best practice and tips shared by leading software testing community experts across the globe!

FacebookLinkedInTwitterEmail