Selenium
Intermediate 15 min read Selenium

Implicit vs Explicit Waits

Synchronization is the number one source of Selenium flakes. Learn the difference between implicit and explicit waits, when to use WebDriverWait with expected conditions, and how FluentWait gives you fine control over polling and ignored exceptions.

WebDriverWaitExpectedConditionsimplicit waitfluent waitstale elementflaky
Introduction

What is this?

An implicit wait tells the driver to poll the DOM for a period whenever findElement finds nothing; an explicit wait pauses execution until a specific expected condition — element visible, clickable, text present — is met using WebDriverWait and expected conditions. Implicit and explicit waits should not be mixed in the same suite. FluentWait adds control over poll frequency and ignored exceptions for slow, dynamic pages. Explicit waits are the production default because they target exactly what the test depends on and stop the moment it is true.

Motivation

Why do you need it?

  • Modern pages load asynchronously — DOM ready does not mean the element you need exists or is interactable.
  • Implicit waits poll globally for any element but cannot wait for conditions like 'clickable' or 'text present'.
  • Explicit waits fail fast with a clear TimeoutException and stop waiting as soon as the condition is true, keeping suites quick.
  • A condition is deterministic, unlike an arbitrary sleep that wastes seconds and still races on loaded CI machines.
  • Waits handle stale elements: a re-render invalidates old references, and waiting re-locates them at the right moment.
Think about it

Mental model

Imagine

Waiting for the coffee machine to finish

Think of

An explicit wait

You do not stand there for exactly five minutes — you check the cup until it is full and stop the moment it is. WebDriverWait polls a condition and returns as soon as it is satisfied, then the test continues without burning extra time.

Mechanics

How it works

  • driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10)) sets a global poll timeout applied to every findElement call.
  • WebDriverWait(driver, Duration.ofSeconds(10)) creates a condition-based poller for a single action.
  • Each expected condition returns a callable that the wait invokes every poll (default 500ms) until it returns a truthy value or the timeout expires.
  • On success the condition's result is returned (often the located element); on timeout a TimeoutException is thrown naming the condition.
  • FluentWait lets you override the poll frequency, timeout and ignored exceptions — e.g. ignore StaleElementReferenceException and keep retrying.

How an explicit wait decides when to proceed.

Action needs element
WebDriverWait created
Condition evaluated
True — proceed
False — poll again
Timeout exceeded
Fast pass or clear fail
Reference

Key commands

chromedriver --log-level=ALL --log-path=wait-debug.log

Capture full driver logs to see the poll cycle when a wait fails.

mvn test -Dtest=WaitsTest

Run a single test class exercising explicit waits.

Tests run: 3, Failures: 0, Errors: 0, Skipped: 0
python -m pytest tests/test_waits.py -v

Run the Python waits suite with per-test output.

pip install selenium

Ensure the Selenium client library is present.

ps aux | grep chromedriver

Check for leaked driver processes after failed runs.

Hands-on

Examples

Waiting for a cart to update in Java

Waiting for a cart to update in Java
import java.time.Duration;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.WebDriverWait;
WebDriver driver = new ChromeDriver();
driver.manage().timeouts().implicitlyWait(Duration.ofSeconds(0));
driver.get("https://example.com/cart");
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
WebElement addToCart = wait.until(ExpectedConditions.elementToBeClickable(By.id("add-to-cart")));
addToCart.click();
wait.until(ExpectedConditions.textToBePresentInElementLocated(
By.className("cart-count"), "3"));
if (driver.findElements(By.className("spinner")).isEmpty()) {
System.out.println("Spinner gone, cart updated");
}
driver.quit();

The implicit wait is set to zero so it never fights the explicit wait. elementToBeClickable returns the button only once it is visible and enabled, then the click proceeds. textToBePresentInElementLocated waits for the cart count to actually show 3, and the spinner check confirms the update finished before the test ends.

Real world

In production

  • Wait for network-driven content (toasts, updated tables and counts) with text-presence or visibility conditions.
  • Wait for clickable buttons so tests never click during a transition state.
  • Use FluentWait to ignore transient stale-element errors while single-page apps re-render.
  • Keep explicit-wait timeouts short (5-15s) so failures surface quickly instead of hanging the whole suite.
Guidelines

Best practices

  • Never mix implicit and explicit waits; pick one model consistently (explicit is recommended).
  • Prefer elementToBeClickable over visibilityOfElementLocated before clicking.
  • Choose the narrowest condition that proves the page reached the state the test needs.
  • Centralize timeout values in one config place so CI and local runs share the same tuning.
  • Avoid Thread.sleep entirely — a wait on a real condition is always better.
Watch out

Common mistakes

  • Setting a 30-second implicit wait and wondering why every failure takes 30 seconds.
  • Using Thread.sleep(5000) everywhere — slow, and still flaky when the machine is loaded.
  • Waiting for visibility of an element that exists but is scrolled off-screen, which never becomes visible.
  • Using the wrong condition (located vs visible vs clickable) for the actual interaction.
Root cause analysis

Common issues

Error
Expected condition failed: waiting for element to be clickable
Root cause

The element is present but covered by an overlay or disabled during the timeout.

Fix

Wait for the blocking overlay to disappear, or wait on the element's enabled state.

Error
TimeoutException: waiting for visibility of element located by ...
Root cause

The element never appeared within the timeout.

Fix

Verify the locator and that the previous step completed; add a wait on the prerequisite state.

Error
StaleElementReferenceException during the wait
Root cause

The DOM re-rendered between polls, invalidating the located reference.

Fix

Ignore stale-element exceptions in a FluentWait and keep polling, or re-locate after the render completes.

When things break

Debugging tips

  • On TimeoutException, take a screenshot and dump page source — is the element missing, hidden or blocked?
  • Reduce the wait timeout locally and run headed to watch the transition the test misses.
  • Log the URL and a few surrounding elements when a wait times out to see where the flow diverged.
  • Check the network tab: a timeout often means the API call the UI depends on failed, not Selenium.
Interviews

Interview checkpoint

Practice

Hands-on lab

Build a Reliable Wait Strategy
30 min · intermediate
In progress
Objectives
  • Reproduce a race condition with a slow-loading fixture
  • Replace fixed sleeps with explicit waits
  • Use multiple expected conditions in one flow
  • Prove stability by running the test repeatedly

Build a Reliable Wait Strategy

30 min · intermediate

0/6 steps
0%
sandbox · Linux sandbox
$
Try these commands
Test yourself

Quick quiz

0/3
1

Which wait keeps retrying a specific condition until it becomes true?

2

You should always combine an implicit wait with explicit waits for maximum reliability.

3

A wait for elementToBeClickable keeps timing out even though the element is visible. What is the best next step?

Quick reference

Cheat sheet

Wait types
implicitlyWait(Duration.ofSeconds(10))Global findElement timeout
WebDriverWait(driver, Duration.ofSeconds(15))Explicit condition wait
FluentWaitCustom poll interval + ignored exceptions
Thread.sleep(...)Never — guess instead of wait
Common expected conditions
elementToBeClickable(loc)Visible and enabled, ready to click
visibilityOfElementLocated(loc)Element is visible
presenceOfElementLocated(loc)Element exists in the DOM
textToBePresentInElementLocated(loc, 'x')Text has appeared
invisibilityOfElementLocated(loc)Loader/overlay is gone
stalenessOf(el)Old element has detached
elementToBeSelected(loc)Checkbox/radio is selected
titleContains('x')Page title updated
Go deeper

Official documentation