Answer
Debugging and Fixing Flaky Selenium Tests
What Makes Tests Flaky?
CODE
Root Causes of Flakiness:
1. Timing/Synchronization — element not ready yet
2. StaleElementReferenceException — DOM changed between find and use
3. Test order dependency — Test B relies on state left by Test A
4. Environment inconsistency — different browser version, network speed
5. Thread safety — parallel tests sharing state
6. Hardcoded sleeps — sleep(3000) too short on slow machine
7. Implicit + explicit wait conflicts — doubled timeouts
8. Element covered by another — ads, spinners, cookie banners
Diagnosis Steps
Step 1 — Capture Evidence
Java
// Add a TestNG listener that screenshots on every failure
@Override
public void onTestFailure(ITestResult result) {
// Screenshot
File screenshot = ((TakesScreenshot) driver).getScreenshotAs(OutputType.FILE);
FileUtils.copyFile(screenshot, new File("fails/" + result.getName() + ".png"));
// Page source
String source = driver.getPageSource();
FileUtils.writeStringToFile(new File("fails/" + result.getName() + ".html"), source);
// Current URL
System.out.println("FAIL URL: " + driver.getCurrentUrl());
System.out.println("FAIL Title: " + driver.getTitle());
}
Step 2 — Add Detailed Logging
Java
// Log what happens at each step
public void click(By locator) {
log.info("Waiting for element: " + locator);
WebElement el = waitForClickable(locator);
log.info("Clicking: " + locator);
el.click();
log.info("Clicked: " + locator);
}
Step 3 — Run in Isolation
Java
// Run the specific flaky test 10 times alone
// testng.xml
<suite name="Flaky Debug" verbose="2">
<test name="FlickyTest" preserve-order="true">
<classes>
<class name="com.tests.PaymentTest">
<methods>
<include name="testCheckoutFlow"/>
</methods>
</class>
</classes>
</test>
</suite>
Common Fixes
Fix 1 — Replace sleep() With Explicit Wait
Java
// ❌ Flaky
Thread.sleep(3000);
driver.findElement(By.id("result")).click();
// ✅ Reliable
wait.until(ExpectedConditions.elementToBeClickable(By.id("result"))).click();
Fix 2 — Wait for Loading Spinners to Disappear
Java
// Wait for spinner to vanish before acting
protected void waitForPageLoad() {
By spinner = By.cssSelector(".spinner, .loading-overlay, [data-loading='true']");
try {
new WebDriverWait(driver, Duration.ofSeconds(3))
.until(ExpectedConditions.visibilityOfElementLocated(spinner));
} catch (TimeoutException ignored) {}
wait.until(ExpectedConditions.invisibilityOfElementLocated(spinner));
}
Fix 3 — Handle StaleElementReferenceException
Java
protected WebElement findWithRetry(By locator) {
for (int attempt = 0; attempt < 3; attempt++) {
try {
return wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
} catch (StaleElementReferenceException e) {
log.warn("Stale element, retrying: " + locator + " attempt " + attempt);
}
}
throw new RuntimeException("Element stale after 3 attempts: " + locator);
}
Fix 4 — Wait for AJAX/Network Calls
Java
// Wait for jQuery AJAX to complete
public void waitForAjax() {
wait.until(driver -> {
Boolean isIdle = (Boolean) ((JavascriptExecutor) driver)
.executeScript("return jQuery.active === 0");
return isIdle;
});
}
// Wait for Angular to stabilize
public void waitForAngular() {
js.executeAsyncScript(
"var callback = arguments[arguments.length - 1];" +
"angular.getTestability(document.body).whenStable(callback);");
}
Fix 5 — Fix Test Order Dependency
Java
// ❌ Depends on previous test
@Test(priority = 2)
public void testCheckout() {
// Assumes testLogin() ran first and left cookies
}
// ✅ Each test sets its own state
@Test
public void testCheckout() {
loginPage.login("alice@test.com", "Pass@123"); // explicit setup
productPage.addToCart("Laptop");
cartPage.checkout();
}
Fix 6 — Dismiss Cookie Banners / Popups
Java
@BeforeMethod
public void dismissCookieBanner() {
try {
WebElement cookieBtn = new WebDriverWait(driver, Duration.ofSeconds(3))
.until(ExpectedConditions.elementToBeClickable(
By.cssSelector("#cookie-accept, .cookie-consent-btn")));
cookieBtn.click();
} catch (TimeoutException e) {
// No cookie banner — that's fine
}
}
Fix 7 — JS Click for Intercepted Clicks
Java
// ❌ org.openqa.selenium.ElementClickInterceptedException
element.click();
// ✅ JavaScript click bypasses element interception
((JavascriptExecutor) driver).executeScript("arguments[0].click();", element);
Retry Mechanism for Critical Tests
Java
// TestNG retry analyzer — re-run flaky tests up to 2 times
public class RetryAnalyzer implements IRetryAnalyzer {
private int count = 0;
private static final int MAX_RETRY = 2;
@Override
public boolean retry(ITestResult result) {
if (count < MAX_RETRY) {
count++;
log.warn("Retrying test: " + result.getName() + " (attempt " + count + ")");
return true;
}
return false;
}
}
// Apply to test
@Test(retryAnalyzer = RetryAnalyzer.class)
public void testFlickyFeature() { ... }
