Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Reliable web automation comes from synchronizing with real application state, using user-facing locators, asserting outcomes, isolating tests, keeping browser flows small, covering the browsers your users run, and preserving enough diagnostics to explain every failure. These practices apply to Selenium, Playwright, and other WebDriver- or browser-based systems.

1. Synchronize with application state, not arbitrary sleep

A fixed delay guesses how long a page will take. Network latency, CPU load, animations, API responses, and CI contention make that guess wrong in both directions: a short sleep races the application, while a long sleep wastes time. Selenium identifies this race as a central browser-automation problem.

Use explicit conditions

Wait for the condition that makes the next command safe: an element becoming visible or enabled, a URL changing, a request completing, a loading indicator disappearing, or a specific text value appearing. Keep the timeout long enough for the supported environment, but short enough to expose genuine failures.

Do not mix Selenium wait styles

Use either an intentional implicit-wait policy or explicit waits, rather than combining them. Selenium warns that mixing implicit and explicit waits can create unpredictable timeout behavior because each command may add its own polling delay.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Prefer Playwright actionability

Playwright actions automatically check conditions such as visibility, stability, and whether an element can receive events. They wait until the configured timeout, then fail with context. You still need an assertion for the result; a successful click only proves that the click was dispatched.

// Playwright (Node.js)
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByText('Saved')).toBeVisible();

Use a short, purposeful wait for a known application signal. Avoid sleeps except for a deliberately tested animation or external system with no observable state, and document that exception.

2. Choose locators that match the user’s view

A locator is also a maintenance contract. Selectors based on generated classes, deeply nested DOM paths, or element order break when a visual refactor leaves behavior unchanged.

Preferred locator order

  • Role and accessible name: buttons, links, headings, dialogs, checkboxes, and their visible names.
  • Label: form controls associated with a visible label.
  • Visible text: stable copy that users can actually read.
  • Placeholder, alt text, or title: when those attributes describe the control reliably.
  • Dedicated test ID: a deliberately defined contract such as data-testid="checkout-submit" when no user-facing attribute is stable.

Why this improves reliability

Playwright locators are designed for auto-waiting and retry-ability. Selenium supports several strategies but cautions that brittle strategies couple tests to implementation details. A role-based locator can survive a CSS rewrite; a selector for .btn-primary-7f3a usually cannot.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
// Playwright
const email = page.getByLabel('Email address');
await email.fill('[email protected]');
await page.getByRole('button', { name: 'Continue' }).click();

When text is expected to change by localization, use a stable accessible label or test ID instead. When repeated controls exist, scope the locator to the relevant row, card, or dialog rather than selecting the first matching element globally.

3. Assert outcomes with retrying web assertions

“The click completed” is an implementation event, not a user-visible outcome. The test should verify what the user is supposed to see next: a confirmation, URL, heading, state transition, downloaded file, or validation message.

Use web-first assertions

Playwright’s web-first assertions wait and retry until the condition is true or the assertion timeout expires. This is safer than reading a value once and asserting the resulting Boolean, which can capture the page between two application states.

await page.getByRole('button', { name: 'Submit order' }).click();
await expect(page).toHaveURL(//orders/d+/confirmation/);
await expect(page.getByRole('heading', { name: 'Order confirmed' })).toBeVisible();

Selenium equivalent

In Selenium, wrap the expected result in an explicit wait and assert after the wait returns. For example, wait for a confirmation element to be visible or for the URL to contain the expected path. Keep the assertion tied to the behavior under test; checking a generic page title can let a broken flow pass.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

4. Isolate every test

Shared state turns one failure into several. A test that leaves a cookie, local-storage flag, database record, or logged-in session behind can change what the next test sees.

Isolate browser state

  • Create a fresh browser context or equivalent session for each test.
  • Give tests independent users, records, and unique names where the application permits it.
  • Reset cookies, local storage, and session storage instead of assuming a previous test cleaned them up.
  • Do not depend on test execution order.

Make reruns reproducible

Seed only the data each test needs, preferably through an API or fixture rather than a long UI setup. If a test fails, rerunning it alone should exercise the same preconditions. Selenium guidance encourages avoiding shared state and starting with a fresh browser per test; Playwright contexts provide a practical isolation boundary.

5. Keep browser flows short and put checks at the cheapest layer

End-user browser tests cover valuable integration paths, but they are expensive to run and diagnose. Selenium describes functional end-user tests as expensive, so do not use a browser to verify logic that a unit or service-level test can check faster.

Split by responsibility

  • Use unit tests for pure validation, formatting, and business rules.
  • Use API or integration tests for permissions, persistence, and service contracts.
  • Use browser tests for a small set of critical journeys and the places where rendering, input, navigation, or accessibility behavior matters.

Design a focused browser test

  1. Arrange the smallest reliable data set.
  2. Perform one discrete user journey.
  3. Assert the externally visible result.
  4. Clean up data that cannot be isolated automatically.

A short flow has fewer timing boundaries and produces a failure message that points to one behavior instead of an entire product tour.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

6. Exercise the browsers and devices your users actually have

A green Chromium run does not establish compatibility with Firefox, WebKit, mobile viewport behavior, or touch input. Define the environments your users support and run a representative matrix.

Playwright projects

Playwright documents projects for Chromium, Firefox, and WebKit. Configure separate projects for the browser engines, viewport sizes, locale, timezone, and any mobile emulation that matters to your product. Run the full matrix on a schedule if every pull request would be too slow, but keep a critical smoke set on each change.

Selenium’s ecosystem

Selenium’s WebDriver ecosystem can target many browser and vendor combinations. Record the exact browser, driver, operating-system, and viewport coverage. A result is meaningful only when its environment is known.

Keep failures attributable

Include the project or capability name in reports. A failure limited to WebKit points investigators toward different causes than one reproduced in every browser.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

7. Make failures diagnosable and maintain dependencies

Retries should provide evidence, not hide defects. Capture a trace, screenshot, console output, network information, and the page URL at failure when your framework supports them.

Use traces on CI retries

Playwright recommends enabling traces on the first CI retry. A trace can reveal the action timeline, locator resolution, screenshots, and network events around the failure. Selenium users should enable equivalent reporting and preserve screenshots, page source, logs, and capabilities.

Classify the failure

  • Timing: the test advanced before application state was ready.
  • Locator drift: the intended control no longer matches.
  • Application defect: the expected state never occurs.
  • Environment: browser, network, service, data, or infrastructure failure.

Fix the underlying category instead of increasing every timeout or adding a retry around the whole suite.

Keep frameworks and browsers current

Playwright recommends updating Playwright so tests run against current browser versions. Pin versions deliberately in CI, upgrade on a schedule, and review release notes for changed browser behavior. For Selenium, maintain compatible WebDriver and browser versions and remove obsolete capabilities.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Selenium or Playwright: which is more reliable?

Neither is universally more reliable. The useful comparison is how each handles the failure modes your suite has.

Axis Selenium Playwright
Synchronization Explicit waits give precise control; avoid mixing implicit and explicit waits. Actions perform actionability checks and wait to the configured timeout.
Locators Broad WebDriver strategies; maintain a stable locator contract. Role, label, text, and test-ID locators are central to retry-ability.
Assertions Build waits around expected conditions, then assert. Web-first assertions retry until the expected condition is met.
Isolation Start a fresh browser or session and avoid shared state. Browser contexts provide an explicit per-test boundary.
Coverage Broad WebDriver ecosystem and vendor options. Projects for Chromium, Firefox, and WebKit.
Diagnostics Use screenshots, logs, page source, and reporting integrations. Trace support and retry-oriented diagnostics are built into the workflow.
Cost and maintenance Browser tests are expensive; ecosystem breadth can mean more compatibility upkeep. Automation reduces boilerplate, while browser and framework updates still require maintenance.

Choose based on your supported browsers, team language, existing infrastructure, and the diagnostics you will actually retain. Applying the seven practices matters more than treating a framework name as a reliability guarantee.

A practical reliability checklist

  • Does every wait name an application condition?
  • Are implicit and explicit Selenium waits kept separate?
  • Can a locator be explained in terms a user would recognize?
  • Does each action have an assertion for its result?
  • Does every test own its data and browser state?
  • Could a unit or API test replace part of this browser flow?
  • Are supported browser and device projects explicit?
  • Will a failed CI run preserve enough evidence to classify the cause?
  • Are framework, driver, and browser versions maintained?
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Troubleshooting flaky tests

“Element not found” or intermittent click failures

Replace sleeps with a locator that waits for the required state. Check whether a dialog, overlay, or navigation changes the accessible name. If the selector depends on generated classes or DOM depth, replace it with a role, label, or test ID.

Timeout after a successful action

The action may have worked while the assertion watched the wrong signal. Assert the resulting URL, visible confirmation, or state change, and inspect the trace or screenshot to see which state was reached.

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Tests pass alone but fail in a suite

Look for shared cookies, storage, records, ports, files, or order assumptions. Create a fresh context and unique data, then run the failing test repeatedly in isolation and in parallel.

Only one browser fails

Record the browser engine and version, reduce the case to one locator and assertion, and verify whether the application itself supports that browser. Do not mask a browser-specific defect with a longer timeout.

Retries pass but the original failure is unexplained

Enable traces or equivalent artifacts on the first retry, retain console and network logs, and classify the failure before changing code. A passing retry is evidence of intermittency, not proof that the test is healthy.

Or skip the browser setup

For a rendered image of a page, ScreenshotNeo provides a single-call screenshot API and MCP server. It accepts consent banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets; each step can be disabled. Bot checks, CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use the API directly:

curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp

Python:

import requests
r = requests.get("https://api.screenshotneo.com/v1/shot", params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"}, timeout=90)
open("shot.webp", "wb").write(r.content)

Node.js:

const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);

See the ScreenshotNeo documentation for parameters. Its MCP server lets Claude, Cursor, and other MCP clients use take_screenshot, get_page_info, and capture_pdf. One thousand screenshots per month are free with no card; paid plans start at $5 for 3,000. Create a free ScreenshotNeo account.

Frequently Asked Questions

Should I increase the timeout when a test flakes?

Only after confirming the application condition is correct. A larger timeout can accommodate a supported slow environment, but it will not fix a wrong locator, missing test data, or an application defect.

Are retries a substitute for fixing flaky automation?

No. Retries help collect diagnostics and reduce transient CI noise; classify and fix the underlying timing, locator, state, application, or environment problem.

How many browsers should run on every pull request?

Run the browsers and devices that represent your support promise. Keep a critical smoke matrix on each change and schedule broader coverage when full execution would slow delivery excessively.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.