Selenium Explicit Waits: When 500ms Polling Beats Thread.sleep
Selenium tests fail when clicking buttons that exist but aren't enabled. Explicit waits with ExpectedConditions solve this by polling the DOM until elements are actually ready, avoiding the slow inefficiency of Thread.sleep or the unpredictable interactions of implicit waits.
22 Dec 2025, 17:30 UTC

The Flaky Button That Broke My Test Suite
In a recent CI pipeline, a login button would intermittently fail with 'element not clickable' errors. The element existed in the DOM, but Selenium tried to click it before it was actually enabled. Using Thread.sleep(5000) fixed the flakiness but made tests unbearably slow. The real solution was explicit waits with proper conditions.
Why Explicit Waits Work
WebDriverWait with ExpectedConditions polls the DOM at regular intervals until a condition is met or a timeout occurs. By default, it checks every 500ms, which balances responsiveness with resource usage. This is far more efficient than fixed delays because it proceeds immediately when the element is ready.
Key Conditions You Need
elementToBeClickable- Element is visible and enabledvisibilityOfElementLocated- Element is present and displayedpresenceOfElementLocated- Element exists in DOM (may be hidden)
Custom Conditions with FluentWait
When built-in conditions aren't enough, FluentWait lets you define custom logic. Set your own timeout, polling interval, and which exceptions to ignore. For example, ignoring StaleElementReferenceException prevents premature timeouts when the page is dynamically updating.
// Wait up to 10 seconds, checking every 250ms
Wait<WebDriver> wait = new FluentWait<>(driver)
.withTimeout(10, TimeUnit.SECONDS)
.pollingEvery(250, TimeUnit.MILLISECONDS)
.ignoring(StaleElementReferenceException.class);
WebElement button = wait.until(d -> {
WebElement el = d.findElement(By.id("submit-btn"));
return el.isEnabled() ? el : null;
});
The Implicit Wait Trap
Configuring both implicit and explicit waits is dangerous. Implicit waits apply globally and can cause WebDriverWait to poll for much longer than intended. The safest approach is to disable implicit waits entirely and use explicit waits only where needed.
Real-World Implementation
Here's a practical pattern for handling a button that becomes enabled after an API call:
public void clickEnabledButton(WebDriver driver, By locator) {
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
// Wait for button to be clickable (visible + enabled)
WebElement button = wait.until(
ExpectedConditions.elementToBeClickable(locator)
);
button.click();
}
// Usage
clickEnabledButton(driver, By.id("login-submit"));
Verification Steps
- Test with a slow-loading element - Create a page where a button enables after 2 seconds and verify your wait proceeds immediately
- Check polling timing - Add
System.currentTimeMillis()logging in a custom condition to confirm 500ms intervals - Simulate failure - Attempt to wait for an element that never appears and confirm the timeout triggers correctly
Trade-Offs and Limitations
Long timeouts mask performance problems. If your test waits 30 seconds for an element that should appear in 2 seconds, you're hiding real issues. Set timeouts based on actual application performance, not convenience. Thread.sleep should never replace explicit waits in production tests—it wastes time and still doesn't guarantee element readiness.
Remember: explicit waits are about waiting for the right condition, not waiting for a fixed amount of time. This makes tests both faster and more reliable.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.