The Nightmare of Random Failures
For months, my Selenium tests were a coin flip. Run them once? Green. Run them again five minutes later? Red. Same code. Same environment. Different results.
The worst part? I couldn't reproduce the failures locally. They only happened in CI, usually at 3 AM, breaking the entire build.
I tried everything: increasing timeouts, adding Thread.sleep() everywhere, praying to the automation gods. Nothing worked consistently.
Then I discovered explicit waits, and everything changed.
The Root Problem Nobody Told Me About
I found this eye-opening blog on TestLeaf that finally explained what was happening. My tests weren't "randomly" failing—they were just too fast.
When I started learning software testing with Selenium, everyone taught implicit waits:
javadriver.manage().timeouts().implicitlyWait(Duration.ofSeconds(10));
"Just set it once and forget it," they said. "It handles everything."
Except it didn't. During my Selenium training in Chennai, I learned the hard truth: implicit waits only help when Selenium can't find an element. They don't help when:
The element exists but is still loading
A spinner is blocking the page
A modal is animating in
The background API call hasn't finished
The One Strategy That Fixed Everything
Explicit waits changed my life. Instead of "wait for anything, everywhere," I started saying "wait for THIS SPECIFIC THING to happen."
Problem 1: The Spinner That Ruined My Life
My test would click "Submit Order" and immediately try to verify the confirmation message. But there was a loading spinner in between that I wasn't accounting for.
What I used to do:
javadriver.findElement(By.id("submitOrder")).click();
Thread.sleep(3000); // Hope 3 seconds is enough
driver.findElement(By.id("confirmationMessage")).getText();
What actually works:
javaWebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
driver.findElement(By.id("submitOrder")).click();
// Wait for spinner to disappear
wait.until(ExpectedConditions.invisibilityOfElementLocated(
By.id("loadingSpinner")
));
// Now it's safe to check the message
String message = driver.findElement(By.id("confirmationMessage")).getText();
The difference? My test now waits for the actual condition (spinner gone) instead of an arbitrary time period.
Problem 2: Modal Pop-ups From Hell
Delete confirmations were the worst. My test would click "Delete," then immediately try to click "Confirm" inside the modal before it fully appeared.
The fix:
javaWebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(10));
// Click delete
driver.findElement(By.id("deleteButton")).click();
// Wait for modal to be visible
WebElement modal = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("confirmModal")
));
// Click confirm inside modal
modal.findElement(By.id("confirmButton")).click();
// Wait for modal to disappear
wait.until(ExpectedConditions.invisibilityOfElementLocated(
By.id("confirmModal")
));
Now my test respects the modal's lifecycle instead of racing through it.
Problem 3: The "Network Idle" Trick
Sometimes data loads asynchronously after the page appears. The table exists, but it's empty until the API responds.
The workaround:
javaWebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(15));
// Wait for table to exist
WebElement table = wait.until(ExpectedConditions.visibilityOfElementLocated(
By.id("orderTable")
));
// Wait for at least one row to appear
wait.until(driver ->
table.findElements(By.cssSelector(".order-row")).size() > 0
);
// Now it's safe to verify data
Not perfect, but way better than sleeping and hoping.
The Results
After switching to explicit waits:
Flakiness dropped 90%. Tests that failed randomly now pass consistently.
CI builds became reliable. No more 3 AM failures that couldn't be reproduced.
Tests ran faster. Explicit waits stop as soon as conditions are met. Thread.sleep() always waits the full duration.
Debugging got easier. When a test fails now, it's usually a real issue, not a timing problem.
My Current Wait Strategy
Here's what I do now for every interaction:
Identify what I'm waiting for - Spinner? Modal? Data loaded?
Use explicit wait for that condition - Don't just hope timing works out
Avoid Thread.sleep() unless absolutely necessary - It's a code smell
Keep implicit waits minimal - I use 2 seconds max, just as a safety net
The Biggest Lesson
When I started doing software testing with Selenium, I thought flaky tests were just part of automation. "Random failures happen," everyone said.
They're not random. They're timing issues. And explicit waits solve 90% of them.
If your Selenium tests are flaky, stop adding more Thread.sleep() calls. Start asking: "What am I actually waiting for?" Then write an explicit wait for that exact condition.
Your future self (and your CI pipeline) will thank you.
Reference: This post was inspired by TestLeaf's comprehensive guide on Selenium explicit waits.
What's your biggest Selenium flakiness challenge? Share in the comments! 👇
Top comments (0)