Back to blog

Your E2E Tests Are Flaky Because You Don't Understand Async

2 min read

If your CI/CD pipeline constantly flashes red because a Cypress test timed out waiting for a button to appear, you have a massive culture problem. But instead of fixing it, what do we usually do? We add another cy.wait(5000) to the code and pray it passes on the next run.

Stop doing this.

A flaky test suite is worse than having no tests at all. When tests fail randomly, developers stop trusting them. And when developers stop trusting the test suite, they start ignoring it. The moment someone says "oh just re-run the pipeline, it's probably that one flaky billing modal test," your entire quality assurance culture is dead.

The Root Cause of Flakiness

End-to-end tests do not fail because the testing framework is buggy. They fail because your frontend code is completely unpredictable.

Look at how you handle async states. When a user clicks "Submit," does the button immediately go into a disabled loading state? Does a spinner appear? Or does the UI just freeze for 400ms while the API request resolves before jumping to the next page?

If your UI doesn't explicitly visually communicate network states, your testing framework has nothing to hook into. Playwright operates much faster than a human. It will click a button, and two milliseconds later it will try to verify the next screen. If your app is still hanging on a slow Promise resolution, the test fails.

Deterministic Testing

You must build testability directly into the source code.

Stop relying on CSS selectors like .btn-primary > span to locate elements. Use explicit data-testid attributes. They signal to other engineers that this element is part of a contract with the test suite.

More importantly, assert on network requests, not just DOM elements. In Cypress or Playwright, you can intercept the exact GraphQL mutation or REST calls. Tell the framework to click the button, explicitly wait for the intercepted API route to return a 200 OK status, and only then assert that the success modal is visible.

Tests should be deterministic. Treat your test code with the exact same architectural rigor as your production code.