A “flaky” visual test is almost never the pixel comparator. It is the page changing
by itself between two captures — and the diff, correctly, points at the difference.
What changes between two captures
- Date and time“3 minutes ago”, “updated today”, a clock in the header.
- Countersvisits, cart, notifications — anything that grows.
- Randomnesslist order, avatar, accent color, “recommended”.
- Render timingweb font arriving late, image loading, animation in flight.
- Live dataprice, stock, reviews — production changes mid-test.
The order that works
- Freeze the data before freezing the pixelPin the test's data source (fixture,
seed, mocked route). A deterministic capture over live data is luck.
- Wait for the final state, not the loadWait for a selector that only exists after
the full render. “networkidle” guarantees neither fonts nor a finished animation.
- Mask what is legitimately dynamicClock, avatar, counter: a declared mask with a
written reason. An over-broad mask turns the test into a rubber stamp.
- Same environment on both endsDifferent font, viewport, device pixel ratio or OS
produce diffs that are not bugs. Compare capture to capture, not to memory.
When the gate is the problem
A layout gate reporting a text collision inside an <a> that sits inside the
<p> is reporting the parent/child pair, not a defect. Before “fixing” the CSS,
measure the fact: scrollWidth == clientWidth at the target viewport. If it holds, the
finding belongs to the gate.
Rule: every false positive you accept without measuring becomes a “fix” that breaks the real
layout.
Before you ship
Run the gate twice in a row on the same machine. If the two runs disagree, the problem is
capture determinism — and no tolerance threshold fixes that without hiding a real bug.
See the Visual Regression Gates skill
See all skills
Want this wired into your project? Guided setup from
R$ 1.500, with the first cycle run together.
← all skills
Get the next article
Sent when I publish, together with the free skill used here. No spam, one-click unsubscribe.
Payment processed by
Stripe — no card data touches this server. Instant delivery once confirmed. Prices in BRL (Brazilian real) — you are charged in BRL.
Terms, delivery and refund.