Beyond cy.wait(): Alternatives for Effective Waiting in Cypress Tests


Cypress's Built-in Waiting Mechanism

Cypress is designed to be resilient and handle asynchronous operations gracefully. It automatically waits for certain events to occur before continuing to the next command in your test. This includes:

  • DOM Mutations
    Cypress waits for DOM elements to be added, removed, or modified before interacting with them. This prevents errors that might occur if you try to interact with an element that hasn't fully loaded yet.
  • Network Requests
    Cypress waits for network requests (like fetching data from an API) to complete before proceeding. This ensures that your test assertions are made on data that has actually been loaded.

When to Use cy.wait()

While Cypress's automatic waiting is often sufficient, there are situations where you might need more control over the waiting behavior. That's where the cy.wait() command comes in.

Using cy.wait()

  • Waiting for DOM Elements
    You can combine cy.get() or other element selection commands with .should('be.visible') or .should('exist') to wait for elements to appear or disappear before interacting with them.

  • Waiting for Network Requests
    If Cypress's default waiting for network requests isn't enough, you can use cy.wait('@alias') where @alias is an alias assigned to an intercepted request using cy.intercept(). This ensures that a particular network request has completed before moving on.

  • Waiting for a Specific Amount of Time
    You can use cy.wait(milliseconds) to explicitly wait for a set duration. However, this approach is generally discouraged as it can make your tests brittle and less reliable. It's better to wait for specific conditions rather than arbitrary timeouts.

Example

cy.intercept('GET', '/api/data').as('getData'); // Intercept the data request
cy.get('button').click(); // Click the button that triggers the request

cy.wait('@getData'); // Wait for the intercepted request to complete

cy.get('.data-container').should('be.visible'); // Assert that the data container is visible

Best Practices for Waiting in Cypress

  • Consider Custom Commands
    For complex waiting scenarios, create reusable custom commands that encapsulate your waiting logic.
  • Use Network Interception
    When dealing with asynchronous operations like network requests, leverage cy.intercept() to create aliases and wait on specific requests using cy.wait('@alias').
  • Prioritize Assertions over Timeouts
    Instead of using timeouts, use assertions like .should('be.visible') to wait for specific conditions. This makes your tests more reliable and easier to maintain.


Waiting for a Specific Amount of Time (Discouraged)

cy.get('input[type="text"]').type('Hello World');  // Type some text
cy.wait(1000);                                      // Wait for 1 second (discouraged)
cy.get('.confirmation-message').should('be.visible'); // Assert confirmation message appears

Waiting for a Network Request

cy.intercept('GET', '/api/users').as('getUsers');

cy.get('button.fetch-users').click();                 // Click button that triggers user fetch

cy.wait('@getUsers');                                   // Wait for the '/api/users' request

cy.get('.user-list').should('be.visible');           // Assert user list is visible after fetching

Waiting for a DOM Element

cy.get('.loading-indicator').should('be.visible');   // Wait for loading indicator

cy.get('.login-form').should('be.visible');          // Then assert login form is visible after loading

cy.get('.loading-indicator').should('not.exist');     // Verify loading indicator disappears
Cypress.Commands.add('waitForElement', (selector) => {
  cy.get(selector).should('be.visible');
});

cy.get('.new-post-form').should('not.exist');  // Verify new post form isn't initially visible
cy.get('button.create-post').click();          // Click button to create a new post

cy.waitForElement('.new-post-form');          // Use custom command to wait for form to appear


Assertions for Waiting

  • Leverage Cypress's built-in assertion commands like .should('be.visible'), .should('exist'), or .should('have.text', 'expected text') to wait for elements to appear, disappear, or have specific content. These assertions automatically retry until the condition is met within the default timeout (configurable with cy.config()). This is generally the preferred method as it makes your tests more readable and verifies expected behavior.

Example

cy.get('.login-form').should('be.visible');   // Wait for login form to become visible
cy.get('#username').type('test_user');
cy.get('#password').type('password123');

Network Interception

  • When dealing with asynchronous network requests, use cy.intercept() to create aliases for expected requests and wait on them with cy.wait('@alias'). This ensures your test proceeds only after the specific network interaction has completed.

Example

cy.intercept('GET', '/api/products').as('getProducts');

cy.get('button.fetch-products').click();  // Click button that triggers product fetch

cy.wait('@getProducts');                   // Wait for the '/api/products' request

cy.get('.product-list li').should('have.length', 5);  // Assert 5 products are displayed

Timeouts (Use with Caution)

  • While not the most recommended approach, you can use cy.timeout() to set a global timeout for all commands in a test. However, this can make your tests brittle if the timing is dependent on factors beyond your control.

Example

cy.timeout(10000); // Set a global timeout of 10 seconds

cy.get('.loading-indicator').should('not.exist');  // Wait for loading indicator to disappear

// Rest of your test steps

Custom Commands (Optional)

  • For complex waiting scenarios, create reusable custom commands that encapsulate your waiting logic. These commands can combine assertions, network interception, or timeouts depending on the specific need.
Cypress.Commands.add('waitForProductAvailability', (productId) => {
  cy.get(`.product[data-id="${productId}"]`).should('be.visible');
});

cy.get('button.add-to-cart').click({ force: true });  // Click add-to-cart button

cy.waitForProductAvailability(123);                  // Wait for product with ID 123 to be visible