Test Automation

90% of Cypress Testers Are Using cy.wait() Wrong — Here’s the Right Way

90% of Cypress testers use cy.wait() wrong. Here's the right way — intelligent waits, retry logic and zero flaky tests. Stop using cy.wait(5000).

4 min read
90% of Cypress Testers Are Using cy.wait() Wrong — Here’s the Right Way
Advertisement

Let’s be honest — we’ve all done it.

You run into flaky tests, and your quick fix looks like this 👇

cy.wait(5000)

Five seconds later — it works!
 …but only sometimes. 😅

Welcome to the illusion of stability that cy.wait() gives.

In this article, let’s break down why most Cypress testers misuse it, how it silently kills reliability, and the right synchronization patterns to make your tests rock-solid. ⚙️


🧨 The Problem with Hard Waits

cy.wait(5000) doesn’t wait for your app to be ready
 it just waits for time to pass.

If the backend is faster → you waste time.
 If it’s slower → your test fails anyway.

You end up with tests that:
 ❌ Pass locally but fail in CI
 ❌ Depend on network speed
 ❌ Hide real synchronization bugs

In short:

cy.wait() is a band-aid for bad synchronization.


✅ What You Should Do Instead

Cypress gives you powerful automatic retry mechanisms.
 Let’s replace cy.wait() with smarter, intent-based waits 👇


🔍 1. Wait for Elements to Appear

Bad 👇

cy.wait(3000)
cy.get('.user-profile').click()

Good 👇

cy.get('.user-profile', { timeout: 10000 }).should('be.visible').click()

💡 Cypress automatically retries cy.get() and .should() until the condition is met — no manual delays needed.


🔄 2. Wait for API Calls to Complete

If you’re testing a flow that depends on network calls,
 use route aliasing and network intercepts.

cy.intercept('GET', '/api/users').as('getUsers')
cy.get('#load-users').click()
cy.wait('@getUsers')
cy.get('.user-list').should('have.length.greaterThan', 0)

cy.wait('@getUsers') waits for the actual request, not an arbitrary time.
✅ Makes tests both faster and stable.


⚙️ 3. Wait for UI State Changes

Don’t guess — assert.

Instead of this 👇

cy.wait(5000)
cy.get('.notification').should('contain', 'Success')

Do this 👇

cy.get('.notification').should('contain', 'Success')

Cypress automatically retries the .should() until the condition is true or the timeout expires.


🧠 Pro Tip: Combine Intercepts with Assertions

Sometimes the UI and API timing differ — fix it by chaining intelligently.

cy.intercept('POST', '/api/login').as('login')
cy.get('#submit').click()
cy.wait('@login').its('response.statusCode').should('eq', 200)
cy.get('.dashboard').should('be.visible')

Here’s what’s happening:

  1. Cypress waits for the network call to finish.
  2. Then it verifies response success.
  3. Finally, it checks UI readiness.

That’s real synchronization. 💪


🧩 Advanced: Dynamic Waiting via Custom Commands

You can abstract your waits for reuse:

Cypress.Commands.add('waitForSpinner', () => {
cy.get('.spinner', { timeout: 10000 }).should('not.exist')
})

Now just call:

cy.waitForSpinner()

Reusable, readable, reliable. ✨


🧱 The Right Waiting Hierarchy

Think of Cypress waits like this pyramid 🧱

Layer Description Example Assertions Retry-based checks .should('be.visible') Intercepts Wait for backend events cy.wait('@api') Custom Commands Abstract complex waits cy.waitForSpinner() Hard Waits Only for debugging cy.wait(5000)

🧠 Use hard waits only for debugging — never in committed tests.


🚀 Bonus: Real-World Example

Let’s fix a flaky signup test.

Before:

cy.visit('/signup')
cy.get('#email').type('qa@test.com')
cy.get('#submit').click()
cy.wait(5000)
cy.get('.success').should('contain', 'Welcome')

After:

cy.visit('/signup')
cy.intercept('POST', '/api/signup').as('signup')
cy.get('#email').type('qa@test.com')
cy.get('#submit').click()
cy.wait('@signup').its('response.statusCode').should('eq', 201)
cy.get('.success').should('contain', 'Welcome')

Result?
 ✅ 4x faster test run
 ✅ 0 flaky retries
 ✅ Confidence restored in CI


🧭 Final Thought

If your Cypress suite feels “flaky,” it’s probably not Cypress’s fault — it’s your waiting strategy.

The goal isn’t to wait more — 
 It’s to wait smarter.

So the next time you reach for cy.wait(5000), ask yourself —

“What exactly am I waiting for?”

Because great testers don’t wait on time — 
 they wait on truth. 💥

Advertisement
Found this helpful? Clap to let Shahnawaz know — you can clap up to 50 times.