Mastering Selenium Explicit Waits: A Practical Guide to WebDriverWait
Flaky Selenium tests? Learn how to use WebDriverWait with ExpectedConditions to wait for specific element states, avoid Thread.sleep, and reduce test flakiness.
28 Sept 2026, 18:17 UTC

The Problem: Flaky Tests When the DOM Isn’t Ready
When a Selenium script runs faster than the page can render, you’ll see sporadic NoSuchElementException or ElementNotInteractableException failures. These “race conditions” happen when AJAX, dynamic CSS, or JavaScript delays leave elements invisible or not yet added to the DOM.
Explicit Waits: The Reliable Solution
Explicit waits tell the driver to pause only until a specific condition is met. The WebDriverWait class polls the browser at a configured interval, checking an ExpectedCondition until either it becomes true or a timeout is reached. Unlike Thread.sleep(), the wait is adaptive and stops as soon as the element is ready.
How WebDriverWait Works Under the Hood
- Creates a
WebDriverWait(driver, timeout)instance. - Polls the page every
pollingIntervalmilliseconds (default 500 ms). - Evaluates the supplied
ExpectedCondition. - Returns the element or throws
TimeoutExceptionif the timeout expires.
Because the wait is scoped to a single element, it’s more efficient and less error‑prone than a global implicit wait. Mixing implicit and explicit waits can double the effective wait time and create unpredictable delays.
Choosing the Right ExpectedCondition
Pick the condition that matches the user interaction you’re testing:
presenceOfElementLocated– the element exists in the DOM, even if hidden.visibilityOfElementLocated– the element is in the DOM and has non‑zero size.elementToBeClickable– visible and enabled, ready for a click.textToBePresentInElement– useful for waiting on dynamic text updates.
When an element is expected to change state (e.g., a loading spinner that disappears), invisibilityOfElementLocated can be used to wait until it’s gone.
Concrete Example: Waiting for AJAX‑Loaded Search Results (Python)
The following script demonstrates how to wait for a container that appears after an AJAX call, then verifies that the first result is clickable. It also intentionally triggers a TimeoutException by setting a very short timeout to show failure handling.
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
# 1. Launch the browser
driver = webdriver.Chrome()
try:
driver.get("https://example.com/search?q=selenium")
# 2. Define a WebDriverWait with a 10‑second timeout
wait = WebDriverWait(driver, 10)
# 3. Wait for the results container to become visible
results_container = wait.until(
EC.visibility_of_element_located((By.ID, "results-list"))
)
# 4. Once visible, locate the first result and click it
first_result = wait.until(
EC.element_to_be_clickable((By.CSS_SELECTOR, ".result-item:first-child"))
)
first_result.click()
# 5. Verify that a detail page loaded (example check)
wait.until(EC.title_contains("Details"))
except Exception as e:
print(f"Test failed: {e}")
finally:
driver.quit()
Execution details:
- Where to run: Any local or CI environment with Python 3.9+ and the Selenium package installed.
- Permissions: The user must have access to the Chrome WebDriver binary.
- Expected check: If the AJAX call returns within 3 seconds, the script proceeds immediately; otherwise it waits until the 10‑second timeout.
- Risk: A timeout value that’s too high can hide performance regressions; too low can cause false failures on slow networks.
Trade‑offs and Limitations
- Implicit vs. Explicit: Mixing them can lead to compounded waits; keep implicit waits to
0when using explicit waits. - Visibility Pitfall:
visibilityOfElementLocatedfails if an element is present but hidden by CSS (e.g.,display:none); usepresenceOfElementLocatedinstead for hidden elements. - High Timeout Masking: Setting a 30‑second timeout on a flaky page may make failures appear intermittent; prefer smaller, realistic timeouts.
- FluentWait Customization: For finer control over polling frequency or to ignore
StaleElementReferenceException, extendWebDriverWaitwithFluentWaitin Java/C# bindings.
Actionable Takeaway
Replace hard‑coded sleeps with WebDriverWait and pick the most specific ExpectedCondition for the element’s intended state. Keep implicit waits disabled, set a reasonable timeout (e.g., 10 s for most UI actions), and verify failures by inspecting the TimeoutException stack trace. This approach dramatically reduces flaky tests and speeds up test suites by avoiding unnecessary waits.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.