Saturday, July 18, 2026

Playwright Retry Mechanism and Flaky Test Handling in Java (Complete Guide)

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 → Passed

The 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