Waiting in Cypress and how to avoid it

January 10th, 2022
| v12.0.0
6 min read
Waiting in Cypress and how to avoid it

There are many perfectionists among testers. Almost everyone I have met has this itch when they use the .wait() command in Cypress and halt the test for a couple of seconds. If this applies to you as well, then you know well that using .wait() like this is not exactly the best solution and try to look for an alternative. The test simply does nothing for a couple of seconds. Those couple of seconds may be enough. But sometimes, the wait is not long enough.

Whenever we use .wait(), we want our application to reach the desired state. Modal closes, network response comes back in, button changes state, etc. Cypress was built with retrybility in mind - which means that as soon as a command passes, it will move on to the next one. If it’s not passing, Cypress will keep retrying for a couple of seconds.

This architecture often causes that Cypress often moves too fast through our application, and we want to make it wait. Reaching for a hard wait is often a way to tell Cypress to slow down. But it’s not ideal, as I already mentioned. So let’s look at a couple of things you can do when you face the dreaded solution.

Just use the wait and be done with it

I know, I know. The heading of this article promises a guide on how to avoid this, but hear me out. Sometimes, the best solution for you and the rest of the team is just using the hard wait. Don’t spend two days finding the right combination of guards, assertions, intercepts and whatnot to avoid using the .wait() command. Those two days are probably exceeding the total waiting time that the test would create. More importantly, your time is much more valuable than the one on CI/CD pipeline. You could be working on something more useful. Perfectionism is expensive. Just add the wait, move on, and come back later. It’s also a good practice to leave a "to do" comment so that anyone that encounters this will get an understanding of why is there a wait in this test.

Copy to clipboard
undefined

PRO TIP: you can use eslint-plugin-cypress to get lint warning every time you use .wait() in your test.

Use "defaultCommandTimeout" to change default timeout

Every element you query for an element using .get() .contains() or some other command, it will have a default wait time of 4 seconds. Cypress will wait for the element to appear in DOM and will retry while it can. If 4 seconds are not enough, you can set the time up globally for your project in the cypress.json file to make Cypress wait longer:

cypress.json
Copy to clipboard
undefined

Setting this timeout has one important side effect. Your tests will fail slower. This may prolong the feedback loop for you, so you might want to reach for a less harsh solution.

Let’s say you have a single test where some elements load slightly slower. Instead of applying the longer timeout globally, you can just apply this configuration in a single test.

Copy to clipboard
undefined

This configuration object works for describe blocks as well:

Copy to clipboard
undefined

Use timeout per command

Prolonging the timeout for the whole test might not always be the best way. Sometimes, you simply want to wait until a certain element appears, but everything else on the page is pretty fast. For these cases, you can use the options object and change timeout for a certain command.

Copy to clipboard
undefined

Notice how we are adding the timeout into our .get() command, not the .should(). The intuitive approach might be to wait for the element to pass our assertion. But our assertion is tied to the querying of the element. That’s why if an assertion is not fulfilled, it will make the whole query as well.

This can also be useful if you want to wait for the element to disappear or be removed from the DOM before you move on to the next step of your test.

Copy to clipboard
undefined

Side note: Be mindful of the difference between not.exist and not.be.visible. I sometimes see people confuse these two and a for good reason. Intuitively, they feel like the same thing. But while not.exist will check for absence of the element in DOM, not.be.visible will only pass if the element is present in DOM, but it is not visible.

Wait for page load

I’ve talked about checking links in the past and why clicking individual links might not be the best solution. But if a page redirect is part of your test flow, you might want to wait a second for the test to continue. Instead of using the wait command, you can use the same principle as in the previous example.

Copy to clipboard
undefined

Wait for API response

Cypress works great with http requests. If you are waiting for some resources to be loaded in your app, you can intercept a request and then create an alias for it. That alias will then be used with .wait() command. Test will only continue once that command is finished.

Copy to clipboard
undefined

Finding the right request to intercept is a great way to make sure that Cypress will wait until page loads with all the right data loaded.

If you need to wait for multiple requests, you can set up a multiple alias wait in a single command:

Copy to clipboard
undefined

One important notice here - if you want to change the default timeout for api responses, you need to work with responseTimeout config option.

Wait for anything

You can wait for basically anything by passing a callback function into .should() command. It will use the built in retry logic and wait for the function to pass. For example, you can wait until all of the elements on page have the proper text. This example shows how we can wait for a list to be reordered instead of waiting for a second.

Copy to clipboard
undefined

These can be applied for anything, for example here we check if input has a proper value and a class:

Copy to clipboard
undefined

Hope you liked this. You can help me spread the word and share this post with your friends if you feel like I deserved it. Make sure to follow me on Twitter or LinkedIn.

Let’s keep in touch

From time to time I send some useful tips to your inbox and let you know about upcoming events. Sign up if you want to stay in loop.

is required.

I treat your email address like I would my own. That means no ads. Just notifications of when I do cool stuff. Unsubscribe anytime. Click here to read about how I handle your data