App & Browser Testing Made Easy

Give your users a seamless experience by testing on 3000+ real devices and browsers. Don't compromise with emulators and simulators

Get Started free
Home Guide How to handle Cypress Asynchronous Behavior?

How to handle Cypress Asynchronous Behavior?

By Pawan Kumar, Community Contributor -

Cypress Async tests – indeed, this is one of the most fundamental ideas we must master. Testers frequently need help understanding how Cypress manages things asynchronously, which can cause problems and misunderstandings afterward. Asynchronous Cypress commands are queued for execution at a later time. 

As commands are carried out, their targets yield to the next command. This is because many beneficial Cypress code checks if everything is in order between each command.

What is Cypress Asynchronous Behavior?

In Cypress framework, the code runs in parallel and waits for each command to complete before moving on without interfering with the state of the preceding command. Even though we wrote our code sequentially, asynchronous code runs immediately and is independent of earlier commands.

For example, if we run a synchronous program, each step will run sequentially, and the subsequent steps will only run once the preceding step has finished running and provided the desired result. But, if you are executing an asynchronous program, the steps won’t depend on one another. Although the program is written sequentially, it will not wait for any step to finish; instead, it will carry out each step without concern for the outcome or condition of the step before it. All of the steps that are available for execution are simply run. 

The difference between a client’s synchronous and asynchronous calls to a server is illustrated below.

difference between a clients synchronous and asynchronous calls to a server

Every Cypress command has an asynchronous nature. Cypress has a wrapper that reads the sequential code we write, enqueues it, and executes it later. So, Cypress fulfills all our tasks related to promises and async nature.

Example:

The example of asynchronous behavior in Cypress is provided below to help you comprehend the idea.

describe('Cypress', function () {
it('Example 1', function (){
// launching Url URL
cy.visit("https://example.cypress.io/")
// identifying element
cy.get('h1').should('have.text', 'Kitchen Sink')
cy.get('h1').then(function(e){
const t = e.text()
// get in Console
console.log(t)
})
// Console message
console.log("Cypress Tutorial")
})
})

The result is provided below:asynchronous behavior in Cypress

Promises to handle Cypress Asynchronous Behavior

Before beginning to execute any commands, Cypress puts them all in a queue. Programming language promises are quite similar to general language promises made to humans. A promise is a condition that represents a person’s behavior or activity. 

Depending on the circumstance and nature, a person can either keep or break the Promise. When the Promise takes place, it is in an irrational state that might either resolve to be fulfilled or become refused.

On the same note, Promise is a state of the command in the case of Cypress async tests, and it will have the following states:

  • Resolved: If the test phase is successful.
  • Pending: If the test phase run result is being awaited.
  • Rejected: If the test phase is unsuccessful.

A Cypress command executes only when the previous step has been executed successfully, or a resolved promise response has been obtained. Then, Promise is added to Cypress using the method.

Example of a Promise in Cypress:

describe('Cypress Test', function () {
it('Promise', function (){
return cy.visit('https://example.cypress.io/')
.then(() => {
return cy.get('h1');
})
})
})

The code in the example above is clear and simple to read and comprehend. All promise-related work is handled in Cypress automation, which keeps it from the user’s view. Thus, we won’t need to be concerned about handling or returning the promises.

Advanced Techniques for Handling Asynchronous Behavior

To avoid Cypress flaky tests, it is essential to have a solid testing strategy for dealing with asynchronous activities. We’ll go through a few best practices for handling this in your Cypress async tests down below.

cy.wait

Before executing the next command, wait a predetermined number of milliseconds or until an aliased resource has resolved.

Syntax:

cy.wait(time)
cy.wait(alias)
cy.wait(aliases)
cy.wait(time, options)
cy.wait(alias, options)
cy.wait(aliases, options)

Arguments:

  • time (Number): The number of milliseconds to wait.
  • alias (String): An aliased route that has been defined using the .as() command and is identified by the @ sign and the alias’s name.
  • aliases (Array): An array of aliased routes that have been defined with the .as() command and are identified by the @ sign and the alias name.
  • options (Object): Use an options object to override cy.wait() default operation
OptionDefaultDescription
logtrueMakes the command visible in the Command log.
timeoutrequestTimeout, responseTimeoutDuration of the wait for cy.wait() to resolve before timing out
requestTimeoutrequestTimeoutChanges the request’s global requestTimeout.
responseTimeoutresponseTimeoutChanges the request’s global requestTimeout.

cy.tick 

After replacing a native time function with cy.clock(), move time. To override native time functions first, cy.clock() must be called before cy.tick().

Syntax:

cy.tick(milliseconds, options)

Arguments:

  • milliseconds (Number): The number of milliseconds needed to move the clock. All timers that fall within the impacted period will be called.
  • options (Object): To modify cy.tick() default behavior, provide an options object.
OptionDefaultDescription
logtrueMakes the command visible in the Command log.

.then()

A command may occasionally yield a subject rather than return it. In that situation, we use .then() to communicate with the subject directly.

Promises and .then() act identically. However, unlike a Promise, .then() is a Cypress command. As a result, we cannot use async/await in the test script.

The callback function’s output becomes the new subject and feeds the command that follows (except for undefined).

cy.get("button").then(($btn) => {
const cls = $btn.attr("class")
// ...
})

.wrap()

In the above example, $btn is a jQuery object. This implies that for Cypress to interact with it and take some action, we must first use cy.wrap().

cy.get("button").then(($btn) => {
const cls = $btn.attr("class")
cy.wrap($btn).click().should("not.have.class", cls)
})

In this example, the <button> HTML element comes first. Our subject, in this example, is the HTML element designated as <button>, which is yielded from cy.get() to .then (). To execute any operations or assertions on the subject, we must first .wrap() it. The subject can then be accessed as the variable$btn.

Cypress must .click() the button before making our assertion, which in this instance is .should(‘not.have.class’, cls). We must first wrap our $btn with cy.wrap() in order to give Cypress the right context to click on it.

Debugging Cypress Asynchronous Behavior

The ability to swiftly identify the root causes of errors and bugs whenever you come across them, is one of a software tester’s most crucial skills. This goes beyond simply writing code quickly. Hence Debugging is crucial in automated programming because of this.

We’ll discuss a few of the most effective techniques for debugging our Cypress async tests and go into great detail on each.

1. Using a Debugger

It can often be very challenging to identify exactly what went wrong and which portion of the code failed to perform as intended while running tests, especially large and complex ones. In cases like this, the debugger is highly beneficial.

Although utilizing the debugger in Cypress is very similar to doing so in other areas of your program (such as the front end), it doesn’t operate similarly.

  • In Cypress, the .then() function is the only place where the debugger can be used. If not, the debugger will stop the test from running before it ever begins.
  • The debugger will suspend test execution after the .then() method ensures that the Cypress command runs and completes.
it('should pause only when the cy.get() executed', () => {
cy.visit('https://example.cypress.io/')
cy.get('h1')
.should('exist')
.then($h1 => {
debugger
})
})

In this section of Cypress debugging, the debugger will only pause the test if the h1 element exists in the DOM since we are saying that the debugger should only pause the test only if the h1 element exists in the DOM.

The result is provided below:Cy2As you can see, the debugger now only pauses the test once it has located the element and verified that it is present in the DOM; otherwise, it will raise an error.

2. Using the .debug()

You can chain the Cypress debugging shortcut, .debug(), to any other Cypress command across your tests. When it is called, the .debug() function will reveal certain information in the browser’s console.

  • Command Name: The name of the most recent command run before the .debug() function was called.
  • Command Args: The list of arguments passed to the last method before calling .debug().
  • Current Subject: A new variable with the name subject will be generated inside the browser. You can use the console to interact with it. The subject variable, the Cypress command’s return value, can be interacted with using the browser console.

We’ll get the first H1 element on the page for this example.

it('should pause the test by using the .debug() command', () => {
cy.visit('https://example.cypress.io/')

cy.get('h1')
.should('exist')
.debug() // debugger
})

The result is provided below:

Cy3

  • We can interact with the current subject that is returned by cy.get() in the console by using the exposed variable subject in our developer tools.
  • During the test, We can easily check any or many areas of our application by using the .debug() function. To view the system’s current state, we can connect it to any Cypress set of commands.

3. Using the Developer Tools

Another method for debugging our code and understanding what occurs when running tests is using the console logs provided by Developer Tool. 

  • The cy.log() command and the standard JavaScript console.log() function are the two commands, we can use to log outputs into the console of our browser. 
  • To console.log(), we should do any Cypress command’s returned value inside the .then() method.
  • The proper method for logging the return value of any Cypress command is to do so inside of .then() function. In this manner, after we log it, we will receive the actual element.

This is the proper course of action.

it('should return the actual h1 element', () => {
cy.visit('https://example.cypress.io/')

cy.get('h1').then($h1 => {
// this will log the actual value of the "h1" element
console.log($h1)
})
})

The result is provided below:Using the Developer Tools in CypressConclusion

This article has examined asynchronously loading browser components as well as the linkage between Cypress and asynchronous loading. When testing Cypress, we discussed various approaches to dealing with asynchronous code. We examined Cypress’ internal handling of promises, which allowed us to create clearer and more comprehensible tests. Also, we looked at methods for debugging Cypress programs.  

It is easier to integrate the Cypress test suite with Browserstack, which will give teams a clearer understanding of the test outcomes. Understand that Cypress testing must be executed on real browsers for accurate results. Start testing on 30+ versions of the latest browsers across Windows and macOS with BrowserStack.

Start Cypress Testing

Tags
Automation Testing Cypress

Featured Articles

How to write an Integration Test with Cypress?

Conditional Testing in Cypress: Tutorial

App & Browser Testing Made Easy

Seamlessly test across 20,000+ real devices with BrowserStack