Skip to content
article

Flaky test bingo: 12 squares I have seen in real life (with fixes)

Print it, put it on your desk, cross squares during the sprint)

Iryna Melnyk@iryna_qa

September 13, 2026 · 3 min read

Hello everyone) I collect flaky test causes like other people collect stamps. Here is my bingo card from the last 4 years. All real, all happened to me or my team. If you get a full row in one sprint, you deserve a day off.

Row 1: Time

  • 1. Test uses real current time. Fails at midnight, at end of month, on DST change. Fix: freeze time. In Playwright page.clock.setFixedTime(), in Jest or Vitest fake timers with a fixed system time.
  • 2. Timezone of CI is not timezone of laptop. CI is UTC, my laptop is Kyiv, so the date is different for 3 hours every night. Fix: set timezoneId in the Playwright config and the TZ environment variable for unit tests.
  • 3. Hardcoded wait. waitForTimeout(3000) works on your machine, CI is 2x slower. Fix: wait for a condition, not for time. Web-first assertions like await expect(locator).toBeVisible() retry by themselves.

Row 2: Data

  • 4. Shared test user. Somebody changed the password, language or role of "qa_main". Fix: every test creates its own data through the API in a fixture.
  • 5. Tests depend on order. Test B passes only because test A created something before it. Run in a different order or shard the suite and it dies. Fix: every test sets up its own state. Playwright runs files in parallel, so this shows up quickly.
  • 6. Unique field collision. Two parallel workers create a user with email test@test.com. Fix: put the worker index and a timestamp into the email, for example 'user-' + testInfo.workerIndex + '-' + Date.now() + '@example.test'.

Row 3: The page

  • 7. Click before the element is ready. Button is visible but disabled while JS loads, or covered by the cookie banner. Fix: Playwright waits for actionability by itself, but it will not close your banner. Close it in a fixture.
  • 8. Animation. Element is still moving when you click. Fix: reducedMotion: 'reduce' in the config, and make your app respect prefers-reduced-motion (real users will thank you too).
  • 9. Locator matches 2 elements. Works while one of them is hidden, fails when both render. Fix: locate by role and name, getByRole('button', { name: 'Save' }). Strict mode will tell you when it is ambiguous.

Row 4: Environment

  • 10. Third-party service inside the test. Payment sandbox is down every second Thursday. Fix: mock it with page.route() in UI tests and test the real integration separately, with a few tests only.
  • 11. Resource limits. CI runner has 2 CPU and 4 GB, your laptop has 10 cores. Too many workers, everything is slow, timeouts everywhere. Fix: set workers for CI explicitly.
  • 12. Free square: "it passed on rerun". This is not a fix. This is a lottery ticket)

How I keep it under control

  • Retries only in CI (retries: process.env.CI ? 2 : 0), and every test that passed on retry goes to a report. Passed on retry is still a failure, just quieter.
  • Quarantine list with owner and date. No owner, no quarantine.
  • Trace on first retry, so when it fails you have DOM, network and console of the exact moment.

Write in comments which square you have this week) I have number 4. Again.

Written by

Iryna Melnyk

QA engineer 🐞 moved from Kharkiv to Lviv in 2022. Manual + automation (Playwright). Love cats and bugs that reproduce from the first time.

Get the best of #flaky_test_society weekly

One email each Monday with the most useful posts. No spam; unsubscribe in one click.

Comments

11

Sign in to join the conversation.

Sign in to comment
Oleksii Bondarenko@oleksii_kyiv#10 in Flaky Test Society
Дуже корисно, Ірино) збережу для нашої команди
5 net votes
Iryna Melnyk@iryna_qa#3 in Flaky Test Society
Дякую, Олексію) сподіваюсь, допоможе
1 net votes
Trần Phương Thảo@thao_qa#4 in Flaky Test Society
I very like number 8, I never think animation can make a test flaky. Also number 6 happen to me last week, 4 workers same email 😅
5 net votes
Siobhan O'Rourke@siobhan_tests#2 in Flaky Test Society
Adding a square: tests that pass alone and fail together because one of them mutates a global (a feature flag, localStorage, a singleton in the app). Bingo for me every quarter.
2 net votes
Iryna Melnyk@iryna_qa#3 in Flaky Test Society
Yes, very good one. Global state is like square 4 but in memory)
4 net votes
Luis Treviño@luis_qa_mty#1 in Flaky Test Society
iryna I have full row 2 this month 🤣 and square 12 every day. great list, sharing with my team
3 net votes
Iryna Melnyk@iryna_qa#3 in Flaky Test Society
Full row! You deserve the day off) and maybe tequila
2 net votes
Sayan Mukherjee@bong_bugs#6 in Flaky Test Society
ami row 3 + free square. bingo. day off kobe pabo? manager ke screenshot pathacchi 🐟
6 net votes
Ollie Grant@404_brain_not_found#10 in Flaky Test Society
mint list. square 10 is our payment sandbox every friday, dead good to know it's not just us
6 net votes
Tahmina Akter@tahmina_qa#10 in Flaky Test Society
Apu, for number 9, is getByRole always better than data-testid? My team uses testid everywhere.
6 net votes
Iryna Melnyk@iryna_qa#3 in Flaky Test Society
Good question) getByRole is closer to how the user sees the page, and it checks accessibility a little bit too. But testid is stable and fine when there is no good role or name. I use role first, testid when role is not possible. Both are better than CSS classes)
2 net votes