DEV Community

Cover image for How to Keep Selenium Tests From Becoming Flaky

How to Keep Selenium Tests From Becoming Flaky

A flaky Selenium test is one that passes and fails inconsistently without any change to the code being tested. Run it ten times and it might fail twice for no clear reason. Over time, these random failures quietly erode trust in test automation teams and start ignoring red builds, re-running suites "just in case," or disabling tests altogether instead of trusting the results.

What Causes Flaky Tests in Selenium?

Most flakiness traces back to a handful of recurring issues. Timing and synchronization problems top the list tests interact with elements before the page has finished loading.

Dynamic elements and unstable locators (like auto-generated IDs or layout-dependent XPath) break easily when the DOM shifts slightly. Shared test data and test-order dependency cause tests to pass or fail depending on what ran before them.

Environment differences between browser or WebDriver versions between CI and local machines introduce inconsistency, and running suites in parallel can create race conditions that only show up under load. Network latency or unstable third-party services add another layer of unpredictability outside the test's control.

How to Avoid Flaky Tests in Selenium

Fixing flaky tests comes down to a handful of deliberate habits rather than one silver-bullet fix. Follow these steps to build a Selenium suite that behaves the same way every time it runs.

Step 1: Use Explicit Waits, Not Fixed Delays

Thread.sleep() is one of the most common causes of flaky tests because it waits a fixed amount of time regardless of what the page is actually doing, too short and the element isn't ready, too long and the suite slows down unnecessarily.

Explicit waits (like WebDriverWait combined with ExpectedConditions) instead wait for a specific condition visibility, clickability, or presence before proceeding.
Avoid mixing implicit and explicit waits in the same test, since Selenium's behavior when both are set becomes unpredictable.

Step 2: Use Stable, Unique Locators

Locators tied to page layout, such as absolute XPath, break the moment a developer tweaks the markup. Prefer unique IDs or dedicated data-test attributes that don't change with styling or structure.

Keeping locators separate from test logic (in a page object or constant file) also makes them easier to update in one place when the UI does change.

Stable locators are especially important in Selenium functional testing, where tests must repeatedly validate user-facing features across different builds.

Step 3: Keep Tests Independent

Each test should set up its own data and state rather than relying on a previous test having run first. Test-order dependency is a common source of flakiness in CI, where execution order isn't always guaranteed. Clean up any data a test creates so it doesn't affect later runs.

Step 4: Handle Dynamic Elements Properly

Wait for AJAX calls, loading spinners, or animations to finish before interacting with an element. Locate elements as close as possible to the moment you interact with them, rather than storing a reference early and reusing it this reduces StaleElementReferenceException errors when the DOM re-renders.

Step 5: Keep Environments Consistent

Mismatched browser and WebDriver versions between local machines and CI pipelines are a frequent, easily overlooked cause of flakiness. Pin versions where possible, and keep test data and external dependencies (like staging APIs) as consistent as the environment itself.

Step 6: Use Retries as a Signal, Not a Fix

Retrying a failed test can help confirm whether a failure is truly intermittent, but retries should never be used to mask an unresolved problem. Log failures with screenshots and stack traces so patterns become visible over time. A test that only passes on retry is telling you something worth investigating, not something to ignore.

Flaky Tests vs. Brittle Tests

Flaky and brittle tests are often used interchangeably, but they point to different problems with different fixes. Knowing which one you're dealing with saves time chasing the wrong root cause.

Flaky Test

Behavior: Passes and fails inconsistently
Trigger: No code changes needed to see it fail
Root cause: Usually timing or environment issues
Fix approach: Stabilize waits, environment, test isolation

Brittle Test

Behavior: Fails consistently and predictably
Trigger: Fails whenever the application changes
Root cause: Usually an overly specific locator or tight coupling to implementation
Fix approach: Loosen locators, decouple from implementation details

Conclusion

Flaky Selenium tests are rarely caused by one single issue; they're usually the result of timing gaps, unstable locators, and tests that aren't truly independent. Explicit waits, stable locators, and isolated test design fix the underlying problems rather than papering over them with retries. A reliable suite is one the team can actually trust.

FAQs

1. What is the main cause of flaky Selenium tests?

Timing and synchronization issues interacting with elements before the page or its content has fully loaded.

2. How do explicit waits reduce Selenium test failures?

They pause execution only until a specific condition is met, instead of a fixed delay, so tests proceed exactly when the element is ready.

3. Should flaky Selenium tests be retried?

Retries can help confirm a failure is intermittent, but they shouldn't replace fixing the actual root cause.

Top comments (0)