Flaky Test Diagnosis¶
What was run¶
A real, genuinely non-deterministic scenario — an element that appears after a real random 50-350ms
delay — run 10 times against two different real timeout configurations, to measure actual flakiness rather
than just discuss it abstractly.
// jitter.html: const delay = Math.floor(Math.random() * 300) + 50; setTimeout(..., delay);
it("checks a real randomly-delayed (50-350ms) element with a too-tight 80ms timeout", () => {
cy.visit("/jitter.html");
cy.get("#jitter-target", { timeout: 80 }).should("be.visible");
});
A real, measured 20% pass rate — genuinely flaky, because the element's actual delay (uniformly
50-350ms) sometimes lands under the 80ms timeout and sometimes doesn't, by actual chance each run.
The real fix, measured¶
it("checks the same real randomly-delayed element with a safe 1000ms timeout", () => {
cy.visit("/jitter.html");
cy.get("#jitter-target", { timeout: 1000 }).should("be.visible");
});
Same real page, same real random delay range, only the configured timeout changed — and the real pass
rate went from 2/10 to a clean 10/10. The real failure message from the tight-timeout version is
specific and diagnostic:
AssertionError: Timed out retrying after 80ms: expected '<div#jitter-target>' to be 'visible'
This element `<div#jitter-target>` is not visible per `Element.checkVisibility()`.
Computed: `display: none`, ...
How It Actually Works¶
This lesson's flakiness has a genuinely different root cause than this network's own
Selenium Mastery Path, which distinguished flakiness by real exception type
(NoSuchElementException vs StaleElementReferenceException) because Selenium's waits are explicit and
separate from its queries. Cypress's auto-waiting (Level 1, Lesson 6) collapses "wait" and "query" into one retrying command, which means the single most
useful diagnostic question for Cypress flakiness is usually simpler: is the configured timeout
comfortably larger than the real worst-case delay, or close enough to it that normal variance sometimes
loses? This lesson's real 80ms timeout against a 50-350ms real delay range was guaranteed to fail
whenever the actual random delay happened to exceed it — not a bug in the test's logic, just an
insufficient margin for real, observed variance.
The real error message itself (Element.checkVisibility(), computed display: none) is also diagnostic:
it confirms the element genuinely existed in the DOM but was not yet visible — the same real
"present vs visible" distinction this network's Selenium course drew out explicitly with
is_displayed(), here surfaced automatically in Cypress's own failure text without needing a
separate check.
Common mistakes¶
- Treating a flaky test's symptom (sometimes fails) as proof of a logic bug in the test itself — this
lesson's real test logic never changed between the
2/10and10/10runs; only the margin between configured timeout and real observed delay changed. - Fixing flakiness by retrying the whole test (
cypress-grep's retry plugins, or CI-level retry-on-fail) instead of fixing the underlying timeout margin — retries hide this lesson's real root cause rather than addressing it, and burn real CI time re-running tests that could pass reliably the first time. - Picking an arbitrarily huge timeout "to be safe" without first measuring the real worst-case delay —
an unnecessarily large timeout (far beyond this lesson's real
1000ms, enough margin over a350msreal maximum) just makes a genuinely failing test (one checking for something that will never actually appear) take longer to report its real failure.
Exercise¶
Change jitter.html's real delay range to Math.floor(Math.random() * 2000) + 50 (now up to a real
2050ms) and re-run this lesson's 1000ms-timeout test 10 times. Measure the real new pass rate and
determine the minimum real timeout value that reliably produces 10/10 against this wider, genuinely
real random range — directly applying this lesson's own diagnostic method to a new, unmeasured scenario.