In our previous article, we saw the configurations in Cypress and various options that can be configured in JSON files. This article will understand Cypress Promise and Cypress Asynchronous behaviour with hands-on implementation and examples in our project. We will also discuss how to incorporate awaits in our asynchronous code and some essential functions like wrap()
and task()
. Let’s get started!
Cypress Promise and Cypress Asynchronous:
Cypress Promise and Cypress Asynchronous nature are some of the essential concepts. Like any other Javascript framework, Cypress also revolves around Asynchronous and Promises. Cypress handles all the asynchronous behaviour internally, and it’s hidden from the user. We will use .then()
to handle promises manually in our code. There are external packages like Cypress-promise in npm where we can manipulate the Cypress asynchronous behaviour. We will discuss each of these topics in detail.
Table of Contents
Cypress Asynchronous
As we know, Cypress is based on Node JS. Any framework that is written build from Node.js is asynchronous. Before understanding the asynchronous behavior of Cypress, we should know the difference between synchronous and asynchronous nature.
Synchronous nature
In a synchronous program, during an execution of a code, only if the first line is executed successfully, the second line will get executed. It waits until the first line gets executed. It runs sequentially.
Asynchronous nature
The code executes simultaneously, waits for each step to get executed without bothering the previous command’s state. Though we have sequentially written our code, asynchronous code gets executed without waiting for any step to complete and is completely independent of the previous command/code.
What is asynchronous in Cypress?
All Cypress commands are asynchronous in nature. Cypress has a wrapper that understands the sequential code we write, enqueues them in the wrapper, and runs later when we execute the code. So, Cypress does all our work that is related to async nature and promises!
Let’s understand an example for it.
it('click on the technology option to navigate to the technology URL', function () { cy.visit('https://themachine.science/') // No command is executed //click on the technology option cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img') // Nothing is executed here too .click() // Nothing happens yet cy.url() // No commands executed here too .should('include', '/technology') // No, nothing. }); // Now, all the test functions have completed executing // Cypress had queued all the commands, and now they will run in sequence
That was pretty simple and fun. We now understood how Cypress Asynchronous commands work. Let us dive deeper into where we are trying to mix sync and async code.
Mixing Cypress Synchronous and Asynchronous commands
As we saw, Cypress commands are asynchronous. When injecting any synchronous code, Cypress does not wait for the sync code to get executed; hence the sync commands execute first even without waiting for any previous Cypress commands. Let us look into a short example to understand better.
it('click on the technology option to navigate to the technology URL', function () { cy.visit('https://themachine.science/') //click on the technology option cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img') .click() cy.url() // No commands executed here too .should('include', '/technology') // No, nothing. console.log("This is to check the log") // Log to check the async behaviour }); });
The log is added at the end of the code, which is a sync command. When we run the test, you can see that the log has been printed even before the page is loaded. This way, Cypress does not wait for the synchronous command and executes it even before executing its commands.
If we want them to execute as expected, then we should wrap it inside the .then()
function. Let us understand with an example.
it('click on the technology option to navigate to the technology URL', function () { cy.visit('https://themachine.science/') //click on the technology option cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img') .click() cy.url() // No commands executed here too .should('include', '/technology') // No, nothing. .then(() => { console.log("This is to check the log") // Log to check the async behaviour }); });
What is Cypress Promise?
As we saw above, Cypress enqueues all the commands before execution. To rephrase in detail, we can say that Cypress adds promises(commands) into a chain of promises. Cypress sums all the commands as a promise in a chain.
To understand Promises, compare them with a real-life scenario. The explanation defines the Promise in asynchronous nature too. If someone promises you, they either reject or fulfill the statement they made. Likewise, in asynchronous, promises either reject or fulfill the code we wrap in a promise.
However, Cypress takes care of all the promises, and it is unnecessary to manipulate them with our custom code. As a Javascript programmers, we get curious about using awaits in our commands. Cypress APIs are completely different than we are used to generally. We will look into this a later part of this tutorial in depth.
States of Cypress Promises
Promises have three different states based on the Cypress commands. They are
- Resolved – Occurs when the step/ command gets successfully executed.
- Pending – State where the execution has begun, but the result is uncertain.
- Rejection – Occurs when the step has failed.
As a Javascript programmer, we tend to write promises in our code and return them. For example,
//This code is only for demonstration describe('Cypress Example ', function () { it('click on the technology option to navigate to the technology URL', function () { cy.visit('https://themachine.science/') //click on the technology option cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img') .then(() => { return cy.click(); }) cy.url() .then(() => { return cy.should('include', '/technology') }) }); });
Here, we are returning promises to each of the commands. This is not required in Cypress. Fortunately, Cypress takes care of all the promises internally,and we don’t need to add promises in each step. Cypress has the retry-ability option, where it retries for a particular amount of time for executing the command. We will see an example of a code without including promises manually.
it('click on the technology option to navigate to the technology URL', function () { cy.visit('https://themachine.science/') //click on the technology option cy.get('.fl-node-5f05604c3188e > .fl-col-content > .fl-module > .fl-module-content > .fl-photo > .fl-photo-content > a > .fl-photo-img') .click() cy.url() .should('include', '/technology') }); });
The above code is not clumsy and is easy to read and understand. Cypress handles all the promise work, and it is hidden from the user. So we don’t have to worry about handling or returning the promises anywhere!
How do you use await in Cypress?
As discussed above, Cypress has its way of handling asynchronous code by creating a command queue and running them in sequence. Adding awaits
to the commands will not work as expected. Since Cypress is handling everything internally, I would recommend not adding awaits
to the code.
If you need to add awaits, you can use a third-party library like Cypress-promise that changes how Cypress works. This library will let you use promises in the commands, and use await in the code
Let us understand the ways to use awaits and how not to use them.
You should not use awaits like this
//Do not use await this way describe('Visit the page', () => { (async () => { cy.visit('https://themachine.science/') await cy.url().should('include', '/technology'); })() })
Instead, you can use like this
describe('Visit the page', () => { cy.visit('https://themachine.science/').then(async () => await cy.url().should('include', '/technology') ()) })
This will work for any Cypress commands.
Cypress Wrap
wrap()
is a function in Cypress that yields any object that is passed as an argument.
Syntax
cy.wrap(subject)
cy.wrap(subject, options)
Let us look into an example of how to access wrap()
in our code.
const getName = () => { return 'Horse' } cy.wrap({ name: getName }).invoke('name').should('eq', 'Horse') // true
In the example, we are wrapping the getName
and then invoke the name for it.
Cypress Wrap Promise
We can wrap the promises that are returned by the code. Commands will wait for the promise to resolve before accessing the yielded value and. then proceed for the next command or assertion.
const customPromise = new Promise((resolve, reject) => { // we use setTimeout() function to access async code. setTimeout(() => { resolve({ type: 'success', message: 'Apples and Oranges', }) }, 2500) }) it('should wait for promises to resolve', () => { cy.wrap(customPromise).its('message').should('eq', 'Apples and Oranges') });
When the argument in cy.wrap()
is a promise, it will wait for the promise to resolve. If the promise is rejected, then the test will fail.
Cypress-promise npm
If we want to manipulate the promises of Cypress, then we can additionally use a library or package called Cypress-promise and incorporate it in our code. This package will allow you to convert a Cypress command into a promise and allows you to await or async in the code. However, these conditions will not work before
or beforeEach
the blocks. Initially, we should install the package in our project by passing the following command in the terminal.
npm i cypress-promise
Once installed, the terminal will look something like this.
After installation, we should import the library into our test file.
import promisify from 'cypress-promise'
With this library, you can create and override the native Cypress promise and use awaits and async in the code. You should access the promise with the promisify
keyword. Let us look into an example for the same.
import promisify from 'cypress-promise' it('should run tests with async/await', async () => { const apple = await promisify(cy.wrap('apple')) const oranges = await promisify(cy.wrap('oranges')) expect(apple).to.equal('apple') expect(oranges).to.equal('oranges') });
This was very simple and fun to learn! This way, you can assign asynchronous code in Cypress.
Cypress Async Task
task()
is a function in Cypress that runs the code in Node. This command allows you to switch from browser to node and execute commands in the node before returning the result to the code.
Syntax
cy.task(event)
cy.task(event, arg)
cy.task(event, arg, options)
task()
returns either a value or promise. task()
will fail if the promise is returned as undefined
. This way, it helps the user capture typos where the event is not handled in some scenarios. If you do not require to return any value, then pass null
value.
Frequently Asked Questions
Is Cypress Synchronous or Asynchronous?
Cypress is Asynchronous by returning the queued commands instead of waiting for the completion of execution of the commands. Though it is asynchronous, it still runs all the test steps sequentially. Cypress Engine handles all this behavior.
Is it possible to catch the promise chain in Cypress?
Cypress is designed in a way that we will not be able to catch the promises. These commands are not exactly Promises, but it looks like a promise. This way, we cannot add explicit handlers like catch
.
Hi…I am Aishwarya Lakshmi, completed my B.Tech and have nearly 2+ years of experience in the testing domain. I am a testing enthusiast and passionate about testing and love to explore new things in my field and share them with my peers. I enjoy writing blogs during my free time in the simplest but effective way. As a tester, I like to have things to perfection, so I wish my readers to have the perfect understanding of the technology. I keep myself updated with the new technologies related to testing and spend time understanding them. I am glad to help students understand the concepts in testing.