Have you ever executed your automation suite twice without changing anything, only to see different results?
If the answer is yes, you've encountered flaky tests.
A flaky test sometimes passes and sometimes fails under the same conditions. These inconsistent failures reduce confidence in the automation framework, waste debugging time, and slow down CI/CD pipelines.
Fortunately, Playwright provides powerful synchronization features that minimize flaky tests, while TestNG offers retry capabilities for handling unavoidable transient failures.
In this tutorial, you'll learn how to identify flaky tests, implement retry mechanisms, and build stable Playwright automation frameworks using Java.
What is a Flaky Test?
A flaky test is an automated test that produces inconsistent results without any changes to the application or test code.
For example:
- First execution → ✅ Pass
- Second execution → ❌ Fail
- Third execution → ✅ Pass
Since the application hasn't changed, the failure is caused by timing, synchronization, or environmental issues rather than a real defect.
Common Causes of Flaky Tests
The most common reasons include:
- Slow page loading
- Dynamic UI elements
- Network latency
- Asynchronous JavaScript execution
- Poor synchronization
- Hardcoded waits
- Shared test data
- Browser resource limitations
- External service dependencies
Understanding these causes is the first step toward creating reliable automation.
Why Playwright Has Fewer Flaky Tests
Unlike many traditional automation tools, Playwright automatically waits for elements to become ready before interacting with them.
Playwright waits for elements to be:
- Visible
- Stable
- Enabled
- Ready to receive user actions
This built-in auto-waiting significantly reduces synchronization issues.
Avoid Hard Waits
One of the biggest causes of flaky tests is using fixed delays.
Avoid:
Thread.sleep(5000);Problems with hard waits:
- Tests become slower.
- Five seconds may not always be enough.
- Sometimes the page loads much faster, wasting execution time.
Instead, rely on Playwright's automatic waiting.
Using Playwright's Built-in Waiting
Example:
page.locator("#loginButton").click();Playwright automatically waits until the button is ready before clicking it.
No explicit wait is required.
Waiting for Page Navigation
Instead of adding delays after clicking a button, wait for navigation.
page.waitForURL("**/dashboard");This ensures the test continues only after the expected page is loaded.
Waiting for Elements
When necessary, explicitly wait for an element.
page.locator("#successMessage")
.waitFor();This is more reliable than using arbitrary delays.
Implementing Retry Mechanism in TestNG
TestNG provides the IRetryAnalyzer interface.
Create a retry class.
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++;
return true;
}
return false;
}
}This configuration retries a failed test up to two additional times.
Using RetryAnalyzer
Apply it to your test.
@Test(retryAnalyzer = RetryAnalyzer.class)
public void loginTest() {
page.navigate("https://example.com");
}If the test fails due to a temporary issue, TestNG retries it automatically.
Retry Flow
Example:
Attempt 1 → Failed
↓
Retry 1 → Failed
↓
Retry 2 → PassedThe final execution result is considered successful.
Capturing Screenshots on Failure
Before retrying, capture evidence.
Example:
page.screenshot(
new Page.ScreenshotOptions()
.setPath(Paths.get(
"screenshots/login.png")));Screenshots simplify debugging and help determine whether a failure was caused by the application or the test.
Logging Useful Information
When failures occur, log:
- Browser name
- URL
- Timestamp
- Test method
- Exception message
- Screenshot location
Rich logs make troubleshooting much faster.
Isolate Test Data
Never allow parallel tests to share the same user account or database records.
Instead:
- Generate unique usernames.
- Use isolated test environments.
- Clean up test data after execution.
Shared state is a common source of flaky failures.
Use Stable Locators
Prefer reliable locators.
Good:
page.getByTestId("login-button");Also good:
page.getByRole(
AriaRole.BUTTON,
new Page.GetByRoleOptions()
.setName("Login"));Avoid brittle XPath expressions based on layout or index positions.
Best Practices for Stable Tests
Keep Tests Independent
Each test should be able to run successfully regardless of execution order.
Avoid Chained Dependencies
Do not make one test depend on the success of another.
Each test should set up its own prerequisites.
Use Explicit Assertions
Verify business outcomes, such as:
- Successful login
- Order confirmation
- Payment completion
Avoid weak assertions that only check page titles.
Retry Only Temporary Failures
Retries should handle transient issues, not hide genuine defects.
If a test fails consistently, investigate and fix the root cause rather than increasing the retry count.
Monitor Flaky Tests
Track tests that require frequent retries.
Recurring flaky tests should be prioritized for improvement or refactoring.
Common Interview Questions
What is a flaky test?
A flaky test produces inconsistent results even when the application and test code have not changed.
Does Playwright automatically wait for elements?
Yes. Playwright automatically waits for elements to become visible, stable, enabled, and ready before interacting with them.
Why should you avoid Thread.sleep()?
Fixed waits slow down execution and are unreliable because page load times vary.
What is the purpose of TestNG's RetryAnalyzer?
RetryAnalyzer automatically re-executes failed tests a specified number of times, helping recover from temporary failures.
Should retries replace proper synchronization?
No. Retries are a fallback mechanism. Stable synchronization, robust locators, and isolated test data should always be the primary strategy for reliable automation.
Conclusion
Building a reliable automation framework requires more than writing test scripts—it requires writing stable test scripts.
Playwright's automatic waiting mechanisms eliminate many common synchronization issues, while TestNG's retry capabilities provide a safety net for temporary failures.
In this guide, you learned how to:
- Identify flaky tests
- Understand their root causes
- Use Playwright's built-in synchronization
- Implement a TestNG RetryAnalyzer
- Capture screenshots for debugging
- Follow best practices for reliable automation
By focusing on stability first and using retries only when appropriate, you'll create Playwright automation suites that are faster, more maintainable, and trusted by development and QA teams.
No comments:
Post a Comment