cy.wait() (Test Automation)
Learn cy.wait() (Test Automation) step by step with clear examples and exercises.
Why This Matters
Test automation plays a crucial role in modern software development, ensuring applications are robust, reliable, and meet quality standards. In this lesson, we will delve into the intricacies of cy.wait(), a vital function in Cypress, a popular test automation tool using JavaScript.
Why This Matters
cy.wait() is an essential part of writing effective tests with Cypress. It allows you to pause the execution of your test script, wait for specific conditions, or ensure that asynchronous operations have completed before moving on. This function helps in creating reliable and accurate tests by providing better control over the test flow.
Prerequisites
To fully understand this lesson, you should be familiar with:
- Basic JavaScript concepts (variables, functions, arrays, objects, etc.)
- Node.js and npm (Node Package Manager)
- Cypress installation and setup
- Understanding the basics of test automation and Selenium
- Familiarity with asynchronous JavaScript concepts such as Promises
Core Concept
cy.wait() is a command in Cypress that lets you pause the execution of your tests for a specified duration or wait for an event to occur. It can be used to handle asynchronous operations, such as AJAX calls, network requests, or DOM manipulations, ensuring that your tests are not prematurely completed before these operations have finished.
Syntax and Usage
The basic syntax of cy.wait() is as follows:
cy.wait(time) // wait for the specified number of milliseconds
cy.wait('@alias') // wait for an aliased resource to resolve
cy.wait(['@alias1', '@alias2']) // wait for multiple aliased resources to resolve
cy.wait(time, options) // wait with custom options
cy.wait('@alias', options) // wait for an aliased resource with custom options
Waiting for a specific duration (milliseconds)
When you provide a number as the argument, cy.wait() pauses the test execution for that specified duration in milliseconds. For example:
cy.visit('https://example.com') // visit the website
cy.wait(2000) // wait for 2 seconds before continuing with the next command
Waiting for an aliased resource to resolve
Aliasing a resource allows you to refer to it later in your test script, making it easier to handle complex scenarios involving multiple asynchronous operations. To use cy.wait() with aliases, first, alias the resource using cy.intercept(), and then wait for it to resolve:
// Alias the request for users
cy.intercept('/api/users').as('getUsers')
// Visit the website and wait for the users data to load
cy.visit('https://example.com')
cy.wait('@getUsers') // wait for the 'getUsers' alias to resolve
Waiting with custom options
By providing an object as the second argument, you can customize the behavior of cy.wait(). The most common option is timeout, which lets you specify a maximum duration before giving up and throwing an error:
// Wait for 2 seconds, but throw an error if it takes more than 3 seconds
cy.wait(2000, { timeout: 3000 })
TypeScript Support
If you are using TypeScript with Cypress, you can take advantage of type inference to simplify your code. For example:
// Define types for UserRequest and UserResponse
type UserRequest = { /* ... */ }
type UserResponse = { /* ... */ }
// Alias the request for users and define a TypeScript type for the interception
cy.intercept('/api/users').as<UserRequest, UserResponse>('getUsers')
// Wait for the 'getUsers' alias to resolve and access the request and response objects
cy.wait<UserRequest, UserResponse>('@getUsers').then(({ request, response }) => {
// Use the request and response objects here
})
Worked Example
Let's create a more complex test that demonstrates using cy.wait(). We will write a test for an e-commerce website that fetches product data from an API, filters it based on a specific category, and verifies the correct number of products is displayed:
- Install Cypress and set up a new project if you haven't already:
npm install cypress --save-dev
cypress open
- Create a new spec file,
product_filtering.spec.js, in thecypress/integrationfolder:
describe('Product Filtering', () => {
it('Verifies the correct number of electronics products is displayed', () => {
cy.visit('https://example-ecommerce.com') // visit the e-commerce website
// Alias the request for getting product data
cy.intercept('/api/products').as('getProducts')
// Wait for the product data to load and filter it based on category
cy.wait<any, any>('@getProducts').then(({ response }) => {
const products = response.body.filter(product => product.category === 'electronics')
const productCount = products.length
// Verify that the correct number of electronics products is displayed on the page
cy.get('.product-list li').each((productElement, index) => {
const productName = cy.wrap(productElement).find('.product-name').text()
if (products[index].name === productName) {
// If the current product matches an electronics product, continue checking the next one
return true
} else {
// If the current product does not match an electronics product, fail the test
cy.wrap(productElement).should('not.exist')
}
})
// Verify that all electronics products are displayed on the page
cy.get('.product-list li').should('have.length', productCount)
})
})
})
Common Mistakes
- Not waiting for asynchronous operations to complete: Failing to use
cy.wait()when dealing with asynchronous operations can lead to premature test completion, resulting in false positives or negatives. - Forgetting to alias resources: Aliasing resources makes it easier to handle complex scenarios involving multiple asynchronous operations. Forgetting to alias a resource may result in difficulty waiting for that specific operation to complete.
- Not providing a timeout when using
cy.wait()with aliased resources: If you don't set a timeout, the test will wait indefinitely for the aliased resource to resolve, potentially causing the test to hang or time out. - Using
cy.wait()improperly with custom options: Misusing thetimeoutoption can lead to tests that either wait too long or time out prematurely. - Not handling asynchronous operations that fail but are still being waited on by
cy.wait(): If an asynchronous operation fails and is still being waited on, the test will hang until the timeout is reached or manually stopped. To avoid this, you can use thecy.wrap()function to convert a failed promise into a Cypress command, allowing you to continue with your test even if the asynchronous operation fails:
// Alias the request for getting product data
cy.intercept('/api/products').as('getProducts')
// Wrap the intercepted request in a Cypress command and wait for it to resolve
cy.wrap(cy.request('/api/products')).as('getProducts')
// Wait for the 'getProducts' alias to resolve and get the number of products from the response
cy.wait<any, any>('@getProducts').then(({ response }) => {
const productCount = response.body.length
// Verify that the correct number of products is displayed on the page
cy.get('.product-list li').should('have.length', productCount)
})
- Not handling network errors: If a network request fails, you can handle it using
cy.wrap()and continue with your test:
// Alias the request for getting product data
cy.intercept('/api/products').as('getProducts')
// Wrap the intercepted request in a Cypress command and wait for it to resolve or fail
cy.wrap(cy.request('/api/products')).as('getProducts')
.then((response) => {
// Handle successful response here
})
.catch(() => {
// Handle network errors here, such as timeout or connection issues
})
Practice Questions
- Write a test using
cy.wait()to verify that a user is able to log in successfully on an e-commerce website.
- Alias the request for getting the login form
- Fill out the login form with valid credentials
- Wait for the login process to complete and check if the user is redirected to the dashboard
- Modify the previous worked example to handle a scenario where the product data takes longer than expected to load.
- Add a custom timeout to
cy.wait()when waiting for the product data - If the product data does not load within the specified time, log an error and continue with the test
- Create a test that verifies the correct number of products are displayed on different pages (e.g., page 1, page 2).
- Use pagination links to navigate between pages
- Wait for each page to load before counting the number of products
- Verify that the correct number of products is displayed on each page
FAQ
Q: Can I use cy.wait() with Selenium or Playwright?
A: No, cy.wait() is a Cypress-specific function and cannot be used with other test automation tools like Selenium or Playwright. However, you can achieve similar functionality using the equivalent functions provided by those tools (e.g., WebDriverWait in Selenium).
Q: How do I wait for multiple asynchronous operations to complete?
A: You can use cy.wait() with an array of aliases to wait for multiple asynchronous operations simultaneously. For example:
// Alias multiple requests
cy.intercept('/api/users').as('getUsers')
cy.intercept('/api/products').as('getProducts')
// Visit the website and wait for both aliases to resolve
cy.visit('https://example.com')
cy.wait(['@getUsers', '@getProducts'])
Q: How do I handle a scenario where an asynchronous operation fails but is still being waited on by cy.wait()?
A: You can use the cy.wrap() function to convert a failed promise into a Cypress command, allowing you to continue with your test even if the asynchronous operation fails:
// Alias the request for getting product data
cy.intercept('/api/products').as('getProducts')
// Wrap the intercepted request in a Cypress command and wait for it to resolve or fail
cy.wrap(cy.request('/api/products')).as('getProducts')
.then((response) => {
// Handle successful response here
})
.catch(() => {
// Handle network errors here, such as timeout or connection issues
})
Q: How do I wait for a specific event to occur?
A: You can use cy.get() with an event listener to wait for a specific event to occur on a DOM element:
// Wait for the click event on the login button
cy.get('#login-button').should('be.enabled')
.click({ force: true }) // Click the button, forcing it to trigger the click event
.then(() => {
// Continue with your test after the click event has occurred
})