assert_
glossary ↓ downloads
track: java · selenium
suite 04 · first selenium framework

Smart waits — the flakiness cure

First Selenium framework15 min
as a manual tester

You instinctively wait for the spinner to finish before checking the result. You don't even think about it.

in automation

Code has no instincts. Tell it exactly what to wait for, or it checks too early and fails randomly. Waits are the #1 difference between suites people trust and suites people ignore.

Modern pages load asynchronously: the HTML arrives, then JavaScript fetches data and builds the rest. Your code runs at machine speed and will happily assert on a page that isn't finished. The result is the flaky test — passes locally, fails in CI, passes on re-run — and it erodes the team's trust in automation faster than anything else.

The wrong fix everyone tries first

the anti-pattern
Thread.sleep(5000);   // wait 5 seconds. always. no matter what.

Thread.sleep() waits a fixed time: too short on a slow day (still flaky), too long on a fast day (a 200-test suite × 5s = 16 wasted minutes per run). It treats the symptom while making the suite slow. You'll see it in legacy suites; now you know why it's there and why to remove it.

The right fix: explicit waits

WebDriverWait polls a condition every half-second and proceeds the moment it's true — failing only if a deadline passes. Fast when the app is fast, patient when it's slow:

ExplicitWaitTest.java
import org.openqa.selenium.support.ui.WebDriverWait;
import org.openqa.selenium.support.ui.ExpectedConditions;
import java.time.Duration;

WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));

// wait until the Products header is visible, then use it — one motion:
WebElement header = wait.until(
    ExpectedConditions.visibilityOfElementLocated(By.className("title")));

System.out.println("Header appeared: " + header.getText());
predict: what does this print? think it through, then reveal
output
Header appeared: Products

The conditions you'll use weekly, all from ExpectedConditions:

What a wait timeout looks like

output — when the condition never comes true
org.openqa.selenium.TimeoutException:
Expected condition failed: waiting for visibility of element located by
By.className: totally-wrong-class (tried for 10 second(s) with 500 milliseconds interval)

Read it like a pro: the condition, the locator, how long it tried. Either the locator is wrong (check $$()), or the app genuinely never showed it (a real bug — congratulations).

One rule to keep suites sane: you'll also meet implicit waits (a global find-element timeout). Mixing implicit and explicit waits causes unpredictable delays — the official docs warn against it. The professional convention: explicit waits only.
⚠ when it breaks — the classics
The wait times out, every time
org.openqa.selenium.TimeoutException: Expected condition failed: waiting for visibility of element located by By.id: finsh (tried for 10 second(s))

Fix: Two possibilities, in order: the locator is wrong (verify in $$() — here it's a typo, 'finsh'), or the app genuinely never showed it — which is a real bug. The timeout message hands you both the condition and the locator.

Waits behave randomly since I 'added both kinds'
(no exception — just unpredictable delays and occasional weird timeouts)

Fix: You've mixed implicit and explicit waits; the official docs warn the combination causes unpredictable wait times. Delete the implicit wait. Convention: explicit only.

I wait for presence, then getText() returns empty
expected: <Hello World!> but was: <>

Fix: presenceOfElementLocated fires when the node exists in the DOM — possibly before it's visible or populated. You wanted visibilityOfElementLocated or textToBePresentInElementLocated.

⚡ exercise · hunt a real race condition

Saucedemo is fast, so use the practice site built for this: the-internet.herokuapp.com/dynamic_loading/1 — a Start button reveals "Hello World!" after a 5-second spinner.

  1. Automate it with no wait: click #start button, immediately getText() on #finish h4. Run it — you'll get an exception. Which one, and is it a Failure or an Error?
  2. Fix it with visibilityOfElementLocated and assert the text equals "Hello World!".
  3. Bonus: also wait for invisibilityOfElementLocated(By.id("loading")) first — the spinner pattern you'll use on every real app.
key takeaways
  • Async pages + machine-speed code = flaky tests; waits are the cure.
  • Never Thread.sleep(): too short and too long at the same time.
  • WebDriverWait + ExpectedConditions proceeds the moment the app is ready.
  • Convention: explicit waits only — don't mix with implicit waits.
← Finding & acting on elements The Page Object Model →