Skip to main content

Why Selenium Tests Flake: Synchronization Beyond Fixed Sleeps

Use explicit outcome checks and Waitless page signals together, while separating timing failures from shared-state and environment problems.

3 min read
Why Selenium Tests Flake: Synchronization Beyond Fixed Sleeps
On this page

Release checked September 5, 2026
This article now reflects v1.0.3 and the documented behavior of that project. Existing URLs are preserved so older links continue to work.

A button click can start a request, replace part of the DOM, and update a result several moments later. A fixed sleep guesses how long that sequence will take. It slows fast runs and still loses the race on a busy CI worker.

Start with the condition the test actually needs

Selenium explicit waits can evaluate built-in or custom conditions. They are not limited to checking one element. The important question is whether the chosen condition establishes the precondition for the next action.

python
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC

def wait_for_orders(driver):
    WebDriverWait(driver, 15).until(
        EC.text_to_be_present_in_element((By.ID, 'orders-status'), 'Loaded')
    )
    return driver.find_elements(By.CSS_SELECTOR, '[data-testid="order-row"]')

The application’s “Loaded” signal must have a defined meaning. A visible spinner disappearing may be a weaker check than the expected result arriving. Avoid mixing implicit and explicit waits because the combined timeout behavior is harder to reason about.

Where page-level stabilization helps

Waitless wraps supported driver calls and common element actions. Before interaction, it polls configured DOM, network, animation, and layout signals. It can reduce repetitive wait code when the page is still changing, while your test keeps responsibility for business assertions.

python
from selenium import webdriver
from waitless import stabilize

driver = stabilize(webdriver.Chrome())
try:
    driver.get('https://test.example.com/orders')
    rows = wait_for_orders(driver)
    assert len(rows) == 3  # Expected count in this controlled fixture
finally:
    driver.quit()

Stability is a threshold, not a promise

Normal mode waits for DOM and network thresholds; strict mode also requires animation and layout checks, while relaxed mode uses DOM activity. Default network settings allow some requests to remain pending. Instrumentation is validated on waits and navigation, rather than continuously proving the application is ready.

Debug other causes of flakiness separately

Failure patternFirst check
Passes alone, fails in the suiteShared account, mutable fixture, or test ordering
Passes locally, fails in CIResource limits, versions, timing, and environment
Wrong element selectedLocator scope and application semantics
Action repeats after retryIdempotency and prior side effects
Page never settlesLong polling, animations, failed requests, or thresholds

Retries can collect evidence about intermittent behavior, but a passing retry should remain visible. Retrying a purchase or submission is different from retrying a read. Do not add whole-test retries as a substitute for finding the cause.

Adopt incrementally

  • Select a small set of timing-related failures with reproducible fixtures.
  • Compare explicit outcome waits with and without stabilization.
  • Keep application assertions and failure artifacts.
  • Measure first-attempt success separately from eventual success.
  • Review wrapper compatibility before applying it across the suite.

See the 1.0.3 release review for optional signals, diagnostics, and current limitations.

Current project documentation

Dhiraj Das

About the Author

Dhiraj Das is an Automation Consultant with over a decade of experience building systems that expose failures, reduce flakiness, and make complex workflows repeatable. He applies that discipline to AI-agent validation, LLM testing, and postmortems.

He shares small open source utilities from real automation work, including: waitless (flaky tests), sb-stealth-wrapper (bot detection), selenium-teleport (state persistence), selenium-chatbot-test (AI chatbot testing), lumos-shadowdom (Shadow DOM), and visual-guard (visual regression).

Share this article: