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 →