Skip to main content
SeleniumDecoded

Fluent Waits

Configure advanced wait behavior with polling intervals, ignored exceptions, and custom messages.

Selenium 3 & 4 Stable

Fluent waits provide the most control over wait behavior, allowing you to configure polling frequency, which exceptions to ignore, and custom timeout messages.

Basic Fluent Wait

Configuring Fluent Wait
Selenium 3 & 4 Stable
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.NoSuchElementException;
import java.time.Duration;
import java.util.function.Function;
// Create fluent wait with custom configuration
FluentWait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(30)) // Maximum wait time
.pollingEvery(Duration.ofMillis(500)) // Check every 500ms
.ignoring(NoSuchElementException.class); // Ignore this exception
// Use the wait
WebElement element = wait.until(driver -> {
return driver.findElement(By.id("dynamic-content"));
});
// With custom message for debugging
FluentWait<WebDriver> waitWithMessage = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofMillis(500))
.ignoring(NoSuchElementException.class)
.withMessage("Waiting for dynamic content to load");
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import NoSuchElementException
# WebDriverWait in Python is already a fluent wait
# Configure with additional parameters
wait = WebDriverWait(
driver,
timeout=30, # Maximum wait time
poll_frequency=0.5, # Check every 500ms
ignored_exceptions=[NoSuchElementException]
)
# Use the wait
element = wait.until(
EC.presence_of_element_located((By.ID, "dynamic-content"))
)
# Custom condition with lambda
element = wait.until(
lambda d: d.find_element(By.ID, "dynamic-content")
)
// JavaScript uses the standard WebDriver wait with conditions
// Configure timeout and polling through the wait call
const { until } = require('selenium-webdriver');
// Basic fluent wait pattern
async function fluentWait(driver, locator, timeout = 30000, pollInterval = 500) {
const endTime = Date.now() + timeout;
while (Date.now() < endTime) {
try {
const element = await driver.findElement(locator);
if (await element.isDisplayed()) {
return element;
}
} catch (e) {
// Ignore and retry
}
await driver.sleep(pollInterval);
}
throw new Error(`Element not found within ${timeout}ms`);
}
// Usage
const element = await fluentWait(driver, By.id('dynamic-content'));
// Or use built-in wait with custom condition
const element = await driver.wait(async () => {
try {
return await driver.findElement(By.id('dynamic-content'));
} catch (e) {
return null;
}
}, 30000);
using OpenQA.Selenium.Support.UI;
using System;
// Create fluent wait with custom configuration
DefaultWait<IWebDriver> wait = new DefaultWait<IWebDriver>(driver)
{
Timeout = TimeSpan.FromSeconds(30), // Maximum wait time
PollingInterval = TimeSpan.FromMilliseconds(500), // Check every 500ms
Message = "Waiting for dynamic content to load"
};
wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
// Use the wait
IWebElement element = wait.Until(d => {
return d.FindElement(By.Id("dynamic-content"));
});
// WebDriverWait also supports fluent configuration
WebDriverWait webDriverWait = new WebDriverWait(driver, TimeSpan.FromSeconds(30))
{
PollingInterval = TimeSpan.FromMilliseconds(500),
Message = "Custom timeout message"
};
webDriverWait.IgnoreExceptionTypes(typeof(NoSuchElementException));

Polling Intervals

Configure how frequently the condition is checked:

Custom Polling Intervals
Selenium 3 & 4 Stable
// Fast polling for quick elements
FluentWait<WebDriver> fastWait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(5))
.pollingEvery(Duration.ofMillis(100)); // Check every 100ms
// Slow polling for resources/network
FluentWait<WebDriver> slowWait = new FluentWait<>(driver)
.withTimeout(Duration.ofMinutes(2))
.pollingEvery(Duration.ofSeconds(2)); // Check every 2 seconds
// Adaptive polling example
public WebElement waitWithAdaptivePolling(By locator) {
// Start with fast polling, increase if taking longer
for (int pollMs : new int[]{100, 250, 500, 1000}) {
FluentWait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(5))
.pollingEvery(Duration.ofMillis(pollMs))
.ignoring(NoSuchElementException.class);
try {
return wait.until(d -> d.findElement(locator));
} catch (TimeoutException e) {
// Try with longer polling
}
}
throw new TimeoutException("Element not found: " + locator);
}
# Fast polling for quick elements
fast_wait = WebDriverWait(
driver,
timeout=5,
poll_frequency=0.1 # Check every 100ms
)
# Slow polling for resources/network
slow_wait = WebDriverWait(
driver,
timeout=120,
poll_frequency=2 # Check every 2 seconds
)
# Adaptive polling example
def wait_with_adaptive_polling(driver, locator):
"""Start with fast polling, increase if taking longer"""
for poll_sec in [0.1, 0.25, 0.5, 1.0]:
wait = WebDriverWait(
driver,
timeout=5,
poll_frequency=poll_sec,
ignored_exceptions=[NoSuchElementException]
)
try:
return wait.until(EC.presence_of_element_located(locator))
except TimeoutException:
pass # Try with longer polling
raise TimeoutException(f"Element not found: {locator}")
// Fast polling for quick elements
async function fastWait(driver, locator) {
return customFluentWait(driver, locator, 5000, 100);
}
// Slow polling for resources/network
async function slowWait(driver, locator) {
return customFluentWait(driver, locator, 120000, 2000);
}
// Helper function for custom polling
async function customFluentWait(driver, locator, timeout, pollInterval) {
const endTime = Date.now() + timeout;
while (Date.now() < endTime) {
try {
const element = await driver.findElement(locator);
return element;
} catch (e) {
await driver.sleep(pollInterval);
}
}
throw new Error('Element not found');
}
// Fast polling for quick elements
var fastWait = new DefaultWait<IWebDriver>(driver)
{
Timeout = TimeSpan.FromSeconds(5),
PollingInterval = TimeSpan.FromMilliseconds(100) // Check every 100ms
};
// Slow polling for resources/network
var slowWait = new DefaultWait<IWebDriver>(driver)
{
Timeout = TimeSpan.FromMinutes(2),
PollingInterval = TimeSpan.FromSeconds(2) // Check every 2 seconds
};

Ignoring Exceptions

Specify which exceptions to ignore during polling:

Ignoring Exceptions
Selenium 3 & 4 Stable
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.ElementNotInteractableException;
FluentWait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofMillis(500))
// Ignore multiple exceptions
.ignoring(NoSuchElementException.class)
.ignoring(StaleElementReferenceException.class)
.ignoring(ElementNotInteractableException.class);
// Or add all at once
FluentWait<WebDriver> wait2 = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofMillis(500))
.ignoreAll(Arrays.asList(
NoSuchElementException.class,
StaleElementReferenceException.class
));
// Use case: Click element that might be stale
WebElement button = wait.until(d -> {
WebElement el = d.findElement(By.id("submit"));
el.click(); // This line can throw if element becomes stale
return el;
});
from selenium.common.exceptions import (
NoSuchElementException,
StaleElementReferenceException,
ElementNotInteractableException
)
wait = WebDriverWait(
driver,
timeout=30,
poll_frequency=0.5,
ignored_exceptions=[
NoSuchElementException,
StaleElementReferenceException,
ElementNotInteractableException
]
)
# Use case: Click element that might be stale
def click_when_ready(driver):
element = driver.find_element(By.ID, "submit")
element.click()
return element
button = wait.until(click_when_ready)
// Custom wait that ignores specific errors
async function waitIgnoringErrors(driver, condition, timeout = 30000) {
const endTime = Date.now() + timeout;
while (Date.now() < endTime) {
try {
const result = await condition();
if (result) return result;
} catch (e) {
// Check if it's an ignorable error
const ignorable = [
'NoSuchElementError',
'StaleElementReferenceError',
'ElementNotInteractableError'
];
if (!ignorable.some(err => e.name.includes(err))) {
throw e; // Re-throw unexpected errors
}
}
await driver.sleep(500);
}
throw new Error('Condition not met within timeout');
}
using OpenQA.Selenium;
var wait = new DefaultWait<IWebDriver>(driver)
{
Timeout = TimeSpan.FromSeconds(30),
PollingInterval = TimeSpan.FromMilliseconds(500)
};
// Ignore multiple exceptions
wait.IgnoreExceptionTypes(
typeof(NoSuchElementException),
typeof(StaleElementReferenceException),
typeof(ElementNotInteractableException)
);
// Use case: Click element that might be stale
IWebElement button = wait.Until(d => {
IWebElement el = d.FindElement(By.Id("submit"));
el.Click();
return el;
});

Custom Wait Conditions

Create reusable wait conditions:

Custom Wait Functions
Selenium 3 & 4 Stable
import java.util.function.Function;
// Custom condition: Element has specific text
Function<WebDriver, WebElement> elementHasText(By locator, String text) {
return driver -> {
WebElement element = driver.findElement(locator);
if (element.getText().contains(text)) {
return element;
}
return null;
};
}
// Custom condition: Element count
Function<WebDriver, List<WebElement>> elementsCountAtLeast(By locator, int count) {
return driver -> {
List<WebElement> elements = driver.findElements(locator);
if (elements.size() >= count) {
return elements;
}
return null;
};
}
// Custom condition: Page fully loaded
Function<WebDriver, Boolean> pageLoadComplete() {
return driver -> {
return ((JavascriptExecutor) driver)
.executeScript("return document.readyState")
.equals("complete");
};
}
// Usage
FluentWait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(Duration.ofSeconds(30))
.pollingEvery(Duration.ofMillis(500));
WebElement element = wait.until(elementHasText(By.id("status"), "Complete"));
List<WebElement> items = wait.until(elementsCountAtLeast(By.className("item"), 5));
wait.until(pageLoadComplete());
# Custom condition: Element has specific text
def element_has_text(locator, text):
def condition(driver):
try:
element = driver.find_element(*locator)
if text in element.text:
return element
except:
pass
return False
return condition
# Custom condition: Element count
def elements_count_at_least(locator, count):
def condition(driver):
elements = driver.find_elements(*locator)
if len(elements) >= count:
return elements
return False
return condition
# Custom condition: Page fully loaded
def page_load_complete(driver):
return driver.execute_script("return document.readyState") == "complete"
# Usage
wait = WebDriverWait(driver, 30, poll_frequency=0.5)
element = wait.until(element_has_text((By.ID, "status"), "Complete"))
items = wait.until(elements_count_at_least((By.CLASS_NAME, "item"), 5))
wait.until(page_load_complete)
// Custom condition: Element has specific text
function elementHasText(locator, text) {
return async (driver) => {
try {
const element = await driver.findElement(locator);
const elementText = await element.getText();
return elementText.includes(text) ? element : null;
} catch (e) {
return null;
}
};
}
// Custom condition: Element count
function elementsCountAtLeast(locator, count) {
return async (driver) => {
const elements = await driver.findElements(locator);
return elements.length >= count ? elements : null;
};
}
// Custom condition: Page fully loaded
async function pageLoadComplete(driver) {
const state = await driver.executeScript('return document.readyState');
return state === 'complete';
}
// Usage
const element = await driver.wait(
elementHasText(By.id('status'), 'Complete'),
30000
);
// Custom condition: Element has specific text
Func<IWebDriver, IWebElement> ElementHasText(By locator, string text)
{
return driver => {
try
{
IWebElement element = driver.FindElement(locator);
return element.Text.Contains(text) ? element : null;
}
catch
{
return null;
}
};
}
// Custom condition: Element count
Func<IWebDriver, IList<IWebElement>> ElementsCountAtLeast(By locator, int count)
{
return driver => {
IList<IWebElement> elements = driver.FindElements(locator);
return elements.Count >= count ? elements : null;
};
}
// Custom condition: Page fully loaded
Func<IWebDriver, bool> PageLoadComplete()
{
return driver => {
return ((IJavaScriptExecutor)driver)
.ExecuteScript("return document.readyState")
.Equals("complete");
};
}
// Usage
var wait = new DefaultWait<IWebDriver>(driver)
{
Timeout = TimeSpan.FromSeconds(30),
PollingInterval = TimeSpan.FromMilliseconds(500)
};
IWebElement element = wait.Until(ElementHasText(By.Id("status"), "Complete"));

Fluent Wait vs WebDriverWait

FeatureWebDriverWaitFluentWait
Default polling500msConfigurable
Exception handlingNoSuchElement onlyAny exceptions
Custom messagesNoYes
Generic inputWebDriver onlyAny object
Use caseStandard waitsCustom scenarios

Best Practices

  1. Tune polling interval based on the expected element behavior
  2. Ignore appropriate exceptions but not all exceptions
  3. Add meaningful messages for debugging failures
  4. Create reusable conditions for common patterns
  5. Consider performance - shorter polling = more CPU usage

Next Steps