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
Contents
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
timezoneIdin 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 likeawait 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 respectprefers-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
workersfor 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
Sign in to join the conversation.
Sign in to commentTrần Phương Thảo@thao_qa
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 😅
Siobhan O'Rourke@siobhan_tests
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.
Luis Treviño@luis_qa_mty
iryna I have full row 2 this month 🤣 and square 12 every day. great list, sharing with my team
Sayan Mukherjee@bong_bugs
ami row 3 + free square. bingo. day off kobe pabo? manager ke screenshot pathacchi 🐟
Ollie Grant@404_brain_not_found
mint list. square 10 is our payment sandbox every friday, dead good to know it's not just us
Tahmina Akter@tahmina_qa
Apu, for number 9, is getByRole always better than data-testid? My team uses testid everywhere.
Iryna Melnyk@iryna_qa
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)