Appearance
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
| Cause | Symptom | Fix |
|---|---|---|
| Fixed sleeps / race conditions | passes locally, fails in CI | wait on conditions, not time |
| Brittle locators | breaks on a CSS/markup tweak | data-testid, not layout XPath |
| Shared/dirty state | order-dependent failures | fresh context/data per test |
| Animations & async loads | clicks the "wrong" element | auto-wait for actionable/stable |
| Network/3rd-party calls | random timeouts | stub them (Module 17 WireMock) |
| Time/locale/randomness | passes some days | inject 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 changesIsolation
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.zipPublish 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
The biggest single cause of UI-test flakiness is…
Which locator is most resistant to flakiness?
How should tests be isolated?
A sensible cross-browser strategy is…
Auto-retrying a flaky test is…
Next: Module 19 · Specialized & Non-Functional Testing. This module's lab is in labs/src/main/java/com/jse21/m18_ui/.