Skip to content

Lesson 04 · Stable UI Tests

Beyond the 1Z0-830 exam

UI tests have a reputation for being flaky — passing and failing without a code change. That reputation is earned by bad practices, not by browsers. This lesson is the discipline that turns a flaky end-to-end suite into a trustworthy one.

Objectives

After this lesson you will be able to:

  • Identify the common causes of flaky UI tests.
  • Apply the fixes: condition-based waits, stable locators, isolated state.
  • Run headless and cross-browser sensibly.
  • Capture screenshots/video/traces on failure for diagnosis.

What makes UI tests flaky

CauseSymptomFix
Fixed sleeps / race conditionspasses locally, fails in CIwait on conditions, not time
Brittle locatorsbreaks on a CSS/markup tweakdata-testid, not layout XPath
Shared/dirty stateorder-dependent failuresfresh context/data per test
Animations & async loadsclicks the "wrong" elementauto-wait for actionable/stable
Network/3rd-party callsrandom timeoutsstub them (Module 17 WireMock)
Time/locale/randomnesspasses some daysinject Clock, fix seed/locale (Module 14)

The throughline: a UI test must be deterministic and independent — the FIRST principles from Module 14 apply directly.

Wait for conditions, never the clock

java
// ❌ Flaky: too short on a slow CI box, wasteful on a fast one.
Thread.sleep(2000);
element.click();

// ✅ Selenium: wait for the actual condition.
new WebDriverWait(driver, Duration.ofSeconds(10))
    .until(ExpectedConditions.elementToBeClickable(submit)).click();

// ✅ Playwright: auto-waits — just act.
page.locator("#submit").click();

Stable locators

Ask the app team for test hooks (data-testid) and locate by them. They're invisible to users, immune to restyling, and unambiguous:

java
By.cssSelector("[data-testid='checkout']")     // robust
By.xpath("//div[3]/section/button[2]")          // breaks the moment layout changes

Isolation

Each test must start from a known state and not depend on another test's side effects:

  • Fresh BrowserContext (Playwright) or new session per test — no leaked cookies/login.
  • Seed and clean test data (Module 16) so a test isn't relying on data another test created.
  • Don't share mutable page objects across tests.

This is exactly why the lab builds a new LoginApp per test — no state bleeds between cases.

Headless and cross-browser

  • Headless (no visible window) is faster and required on most CI agents. Run headed locally to debug, headless in CI — but know that a few bugs only reproduce in one mode.
  • Cross-browser: run the suite across Chromium/Firefox/WebKit via a CI matrix (Module 20). Don't multiply every test across browsers — keep a smoke subset cross-browser and the bulk on one.

Diagnose failures with artifacts

A headless CI failure you can't see is hard to fix. Capture evidence at failure time:

java
// JUnit: take a screenshot when a test fails (e.g. in an extension / @AfterEach on failure)
byte[] png = ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
// Playwright: page.screenshot(...), per-context video, or a trace.zip

Publish these as CI artifacts (Module 20) so the failing page state is one click away.

Gotcha — don't just retry flaky tests

A retry (rerun-on-failure) can mask flakiness, but a test that needs retries is telling you about a real race — in the test or the app. Use retries as a stopgap and a signal to investigate, never as the cure. Quarantine persistently flaky tests; don't let them erode trust in the suite.

Key Takeaways

  • Flakiness comes from timing, brittle locators, shared state, and un-stubbed network — not from browsers themselves.
  • Wait on conditions (or Playwright auto-wait), never Thread.sleep.
  • Locate by stable test hooks (data-testid); isolate state and data per test.
  • Run headless in CI, keep a cross-browser smoke subset, not everything.
  • Capture screenshots/video/traces on failure and publish them as artifacts.
  • Retries mask, they don't fix — treat a flaky test as a real race to investigate.

Lesson Quiz

Lesson Quiz · Stable UI Tests0 / 5
  1. The biggest single cause of UI-test flakiness is…

    • AToo many assertions
    • BTiming — fixed sleeps and races instead of waiting on conditions
    • CUsing page objects
    • DRunning headless
  2. Which locator is most resistant to flakiness?

    • AAbsolute XPath like //div[3]/button[2]
    • BA data-testid attribute the app guarantees
    • CA CSS class used for styling
    • DElement index
  3. How should tests be isolated?

    • AShare one login session across all tests
    • BStart each test from a known state (fresh context/session, seeded+cleaned data)
    • CDepend on prior tests' data
    • DRun them in a fixed order only
  4. A sensible cross-browser strategy is…

    • ARun every test in every browser
    • BRun a smoke subset cross-browser and the bulk on one browser
    • CNever test more than one browser
    • DOnly test in headed mode
  5. Auto-retrying a flaky test is…

    • AA proper fix
    • BA stopgap that masks a real race — investigate and quarantine, don't rely on it
    • CAlways wrong to use
    • DFaster than fixing locators

Next: Module 19 · Specialized & Non-Functional Testing. This module's lab is in labs/src/main/java/com/jse21/m18_ui/.