DriversRecommendedOutdated drivers can make a good PC feel brokenScan driver issues before chasing fixes manually.Scan NowHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
Skip to content
World desk7 min

Why Hard Waits Make Test Automation Unreliable (and What to Wait For Instead)

A hard wait pauses a test for a guessed time. Here is why that causes flaky, slow tests and which condition-based waits to use in Selenium, Cypress and Playwright.
Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

A hard wait, such as Thread.sleep(5000) or cy.wait(5000), pauses the test for a fixed time without checking whether the application is ready. That is why it is unreliable: if the app needs longer than the pause, the next step runs too early and fails; if it needs less, the test still sits out the whole delay. The fix is to wait for the specific state the next step needs, and to use timeouts only as upper limits.

What a hard wait actually does

A fixed sleep encodes a guess about duration, not a condition about the application. Selenium’s documentation describes the underlying problem as a race: the automation command can execute before a dynamic page has reached the state the test needs. A page’s readyState does not necessarily mean that JavaScript-driven changes have finished. Selenium calls this one of the primary causes of flaky tests (Selenium Project, Waiting Strategies, page last modified September 3, 2024). A sleep tries to win that race by guessing, and a guess fails in both directions:

As an Amazon Associate I earn from qualifying purchases.

  • Too short: the race remains. The test passes on a fast machine and fails on a loaded CI runner.
  • Too long: you pay the full delay every time that line runs, even when the app was ready in 200 ms. Across a suite, these fixed costs add up.

Cypress’s performance guidance is blunt about the remedy: if you find yourself reaching for cy.wait(number), the right fix is almost always to add an explicit assertion that Cypress can retry (Cypress, Optimizing test performance). Both statements are project documentation, not individual opinions.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why the failures look random

A hard wait makes the outcome depend on timing you do not control: server load, cold caches, CPU contention on shared CI, network latency, and test order. The test is correct on one run and wrong on the next without any code change, which is the definition of a flaky test. Raising the sleep value usually just moves the failure rate lower while making every run slower. No public statistic quantifying the effect of hard waits was found in the official documentation, so treat any specific percentage you see quoted elsewhere with caution unless it cites a primary study.

What to wait for instead

Ask what the next step really requires, then wait for exactly that. Typical states are an element appearing, becoming visible, becoming actionable, or showing expected text.

Selenium: explicit waits

Selenium supports implicit and explicit waits. An explicit wait polls for a condition and returns as soon as it is true, up to a timeout.

// Java
WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
WebElement toast = wait.until(
    ExpectedConditions.visibilityOfElementLocated(By.id("saved-toast")));
assertEquals("Saved", toast.getText());
# Python
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.common.by import By

toast = WebDriverWait(driver, 10).until(
    EC.visibility_of_element_located((By.ID, "saved-toast")))
assert toast.text == "Saved"

Here 10 seconds is a ceiling. If the toast shows after 300 ms, the test continues after about 300 ms.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Do not mix implicit and explicit waits. Selenium warns that combining them can cause unpredictable wait times, and its documentation gives an example where the combined settings can exceed the apparent explicit timeout. Pick explicit waits and leave the implicit wait at zero. This warning is specific to Selenium/WebDriver.

Cypress: retryable queries and assertions

Cypress retries queries and assertions automatically, so the assertion itself is the wait:

// Instead of: cy.wait(3000)
cy.get('[data-test=results] li').should('have.length.greaterThan', 0)
cy.contains('[data-test=status]', 'Saved').should('be.visible')

Cypress actions also wait for elements to be actionable, as described in its core concepts. For a known slow operation, adjust the relevant timeout rather than adding a delay:

cy.get('[data-test=report]', { timeout: 30000 }).should('be.visible')

Cypress’s performance page gives a default command timeout of four seconds; that figure is framework-specific and can change between versions, so check your own version.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Playwright: auto-waiting and web-first assertions

Playwright checks actionability conditions before performing actions such as clicks (see Auto-waiting), and its web-first assertions retry until the expected state is reached (see Writing tests).

await page.getByRole('button', { name: 'Save' }).click();   // waits until actionable
await expect(page.getByText('Saved')).toBeVisible();        // retries until true or timeout

Avoid replacing these with page.waitForTimeout(), which reintroduces the fixed pause.

Waiting on a network request

Sometimes the real synchronization point is a specific request. Cypress documents aliasing a route and waiting for that alias (see Migrate from Selenium to Cypress):

cy.intercept('GET', '/api/orders*').as('getOrders')
cy.visit('/orders')
cy.wait('@getOrders')
cy.get('[data-test=order-row]').should('have.length', 10)

Note the last line. A completed request is not proof that the page rendered the right content, so follow the wait with an assertion on what the user sees. That pairing is our reading of how Cypress’s own examples are structured.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Framework comparison

Question Selenium Cypress Playwright
Condition-based wait Explicit waits (WebDriverWait) Queries and assertions retry Web-first assertions retry
Readiness before actions You must wait explicitly Actions wait for actionability Actions check actionability
Waiting on requests Not built into the wait API; wait on the resulting UI Alias a route, then cy.wait('@alias') Possible, but pair with a UI assertion (not covered by the sources used here)
Global setting risk Implicit wait; do not mix with explicit Timeouts are upper bounds Timeouts are upper bounds

Their semantics are not interchangeable, so learn how your tool defines “ready” before porting a wait pattern.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Are all fixed delays wrong?

The official sources strongly discourage arbitrary waits as a way to establish UI readiness. They do not publish a universal list of exceptions. If a test truly must model elapsed time and the app offers no observable signal, keep that delay narrow, named, and commented, and never use it as general synchronization. In most cases a signal exists: a spinner disappearing, a row count, a status text, or a response.

Migrating a suite away from sleeps

  1. Search the codebase for sleep, Thread.sleep, cy.wait( with a number, and waitForTimeout.
  2. For each, write down what the next line needs: visible element, enabled button, text, row count, or a finished request.
  3. Replace the delay with that condition, as an assertion or explicit wait.
  4. If the operation is legitimately slow, raise the timeout for that one condition, not globally.
  5. Run the test repeatedly, ideally on a throttled CI-like runner, to confirm it no longer depends on timing.

Troubleshooting

Symptom Likely cause Fix
Explicit wait takes far longer than its timeout in Selenium Implicit wait also set Set implicit wait to 0 and use only explicit waits
Test passes after the request finishes but asserts wrong data Request completion treated as rendered result Assert on the visible outcome after the wait
Condition never becomes true Wrong locator or state (visible vs present) Check the exact condition; inspect the DOM at failure
Suite slow despite no obvious sleeps Timeouts set very high globally, or retries on failing conditions Use per-operation timeouts; fix the cause of failure
Wait removed but test still flaky Asserting a state that precedes the real one (e.g. element exists but is still re-rendering) Wait for a later, user-visible signal

Visual tests and screenshots: the same trap

Screenshot checks are a common place for hidden sleeps (“wait 3 seconds, then capture”). The cure is the same: wait for a selector, network idle, or a specific state, then capture. A screenshot taken during a cookie banner or half-loaded page is just another flaky result.

Or skip the browser setup

If you only need a clean capture of a page, ScreenshotNeo is a screenshot API with options to wait for a selector, a delay or network idle, so you don’t need your own browser and sleeps. Cookie and consent banners, newsletter popups and chat widgets are removed before the shot, each step can be turned off. See the docs for all options.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
  • Bot checks, blank pages, timeouts, failed loads and cache hits are never billed; the X-Page-Verdict and X-Billed response headers tell you which it was.
  • An MCP server (tools take_screenshot, get_page_info, capture_pdf) lets AI agents such as Claude or Cursor take screenshots.
  • 1,000 screenshots a month are free with no card; paid plans start at $5 for 3,000.

Start free with 1,000 screenshots a month, no card required.

Frequently Asked Questions

What should I use instead of Thread.sleep in Selenium?

Use WebDriverWait with an ExpectedCondition that matches what the next step needs, such as visibility or clickability of a specific element.

Is a long timeout the same as a hard wait?

No. A timeout is an upper limit on observing a condition and the test continues as soon as the condition is met. A hard wait always consumes its full duration.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Leave a Reply

Your email address will not be published. Required fields are marked *

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

More from the Wire

  1. World desk4 min
    How to Spot an AI Voice Scam Before Sending MoneyDon’t rely on how a caller sounds. Pause, call back through a known number, and verify the emergency with another trusted person before sending money.
  2. Mountain View desk4 min
    Google’s SynthID Detector: How to Check AI-Generated Images, Video and AudioGoogle’s SynthID Detector looks for an embedded watermark in supported images, video and audio. Here is what its results do—and do not—show.
  3. Redmond desk20 min
    How to create a link to File or Folder in Windows 11Windows 11 gives you several ways to point to a file or folder without moving or duplicating it. You can create a desktop shortcut,…
Recommended PC Tool
Recommended PC Tool
Outdated Drivers Are Slowing You DownFree scan - exact matches
Windows Errors? Fix Them Before They SpreadFree repair scan

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.