Send your request Join Sii

In the work of an automation tester, one of the key tasks is the correct handling of waits for the events, which allows us to perform assertions on an already prepared element. In testers jargon, we encounter the term “waits” from the wait() method, and this nomenclature is used in this article.

In this post, I will elaborate on what waits are needed for in automated tests, present a comparison of waits available in Selenium and in Cypress, along with an example implementation in Cypress.

Cypress significantly stand out from other available applications for automated testing due to one specific wait, which will also be described along with the cy.wait() and cy.intercept() methods.

The last aspect raised in this article will be recursion, which can also be used in Cypress to wait for an item, as its own custom approach.             

Previously on our blog appeared an introductory article on Cypress (only PL). I encourage you to read it to learn about the rest of the tool’s features.

Wait in automated tests

Wait is one of the basic and key parts of an automated test. It allows us to perform a delay in the test to, in order to load the web page, or any element on that page, correctly. This will make it possible to check, that the results in a positive or negative completion of the test.

Wait’s wrong implementation can result in an incorrect test result, e.g. by trying to interact with a page, which has not fully loaded. Another popular problem is lack of an item’s display, because it was waiting, for example, for the server response. However, one of the biggest difficulties connected with the wait’s wrong implementation is the unnecessary waiting, which increases our test duration.

Wait in Selenium

In one of the most popular tools for tests automation, that is Selenium, we encounter three types of waits, which I will describe below.

Implicit wait

It consists of an attempt to find an element in the DOM for a spcidied time if it is not immediately available. The default value of this wait is 0 and when we change this value, the implicit wait lasts until the end of the session.

Selenium example – Implicit wait
Fig. 1 Selenium example – Implicit wait

As presented in the code above, the implementation of this solution is very fast, but it has some limitations. This wait works only with the findElement() method, and that keeps us from waiting to load, for example, the text in the element.

Explicit wait

Thanks to it, we have a possibility to stop the test until our expected condition is met. Checking the condition is triggered with a certain frequency until the timeout expires. This means, that when the condition returns a false value, Selenium will wait and try again.

Selenium example – Explicit wait
Fig. 2 Selenium example – Explicit wait

In the code presented above, we wait until the title of our page gains the expected value. Keep in mind that the wait is now used only at the time of the trigger, and not as previously – globally. Here you will find a list of available expected explicit wait conditions for Selenium.

FluentWait

We wait in an explicit manner for a certain situation for a given number of seconds – for example: the following code for 10 seconds will check if our website has the expected title with the frequency of one second.

Selenium example – FluentWait
Fig. 3 Selenium example – FluentWait

Wait in Cypress

Cypress, by default, uses dynamic wait for elements or actions available on the web page used in our automated test. We can distinguish the following ways to handle waits.

Default timeouts

In Cypress there are 6 default timeouts available. Thanks to the built-in re-try mechanism, they perform checks in a defined by us, or preset time, similarly to the explicit wait previously described:

  • defaultCommandTimeout – it is the time, in which Cypress tries to get to the element or action while executing most of the commands on the DOM.
  • execTimeout – a timeout that defines how long to wait for the system to complete the operations during the cy.exec() command.
  • taskTimeout – the maximum time, in which we wait for a task completion during the cy.task() command.
  • pageLoadTimeout – waits for the “page transition event”. This event occurs when a user visits or leaves a particular web page. This default timeout also waits for the cy.visit(), cy.go(), cy.reload() commands to execute load events. They are triggered when the entire page and all its contents, i.e. style sheet, scripts, iframes and images are loaded.
  • requestTimeout – the maximum time we wait for cy.wait() execution.
  • responseTimeout – waits for the execution of cy.request(), cy.wait(), cy.fixture(), cy.getCookie(), cy.getCookies(), cy.setCookie(), cy.clearCookie(), cy.clearCookies() and cy.screenshot() methods.

If we want to overwrite the values for timeouts, we do it in the cypress.json file:

Cypress – cypress.json set value defaultCommandTimeout
Fig. 4 Cypress – cypress.json set value defaultCommandTimeout

We can also change these times for a given action chain or the method itself by sending the object with an expected time:

Cypress – implicit subject used and overwriting defaultCommandTimeout by sending object to the get method
Fig. 5 Cypress – implicit subject used and overwriting defaultCommandTimeout by sending object to the get method

In the code above we used implicit subject to create assertion with the .should() method. First, Cypress tries to find the button element within the 20 seconds defined in the object sent {timeout: 20000}, and then performs assertion on that button, to check if it contains the text “Search in Google”. Here, again, waits up to 20 seconds for the text to appear. We can obtain the timeout while executing the cy.get() and should() method, which will negate the entire test.

For the get(), find(), cointains() methods, from my experience, I recommend using a timeout on the action chain in case the wait for an item or its properties is extended. Whereas, the timeout set in cypress.json should be limited to a reasonable minimum to avoid unnecessary lengthening of the tests in case we receive a fail.

In Cypress, we have the possibility to make a similar assertion with an explicit subject by using the .then() method and making an assertion on a JQuery element:

Przykład Cypress – asercja z explicit subject
Fig. 6 Cypress example – assertion with explicit subject

In this case, Cypress first waits up to 20 seconds for the .get() method, and then immediately makes an assertion on the element, omitting the defined defaultCommandTimeout.

Built-in Wait

Cypress has its own implementation of the wait method, which takes up to two arguments:

  • One of the first values is a numeric value given in milliseconds. This is the value of time, which elapses between the previous and the next command in the Cypress action chain.
  • The next value we can send in place of time is an alias or an alias table. Aliases are a tremendous construct in Cypress that has many uses and a separate article could be written about it. However, for us, in the waits concept, the most important thing is to focus on the possibility to assign an alias to the API call. We do it through the cy.intercept() and the .as() method, where the as method saves an alias, to which we have access with the use of the @ prefix.
  • The last, optional, value is the option object, which allows us to overwrite the previously defined default times for requestTimeout and responseTimeout. Additionally, it pro {log: true/false} object.
Cypress – ways of cy.wait() implementation
Fig. 7 Cypress – ways of cy.wait() implementation

This code shows the Cypress wait implementation. This method allows us to wait in a specific place in the code for a given time or until an appropriate alias appears.

Creating an alias

In order to properly create a wait for an alias, we need that alias precisely. One method to create it is to look at the queries made by the browser on our test page. Then, we select the last query or the last few queries made, which, with high probability, will show us that the page is loaded.

The list of API queries on the www.google.pl page
Fig. 8 The list of API queries on the www.google.pl page

Once we find the query of interest, all we need to do is use the cy.intercept() method. It allows us to track and manage requests and responses in the network. It allows us to:

  • Mock data
  • Validate API requests
  • And thanks to the combination with cy.wait(), wait for the end of the given API query.

Then, with the .as() method, we can save the tracked request as an alias.

Intercept implementation

Cypress example – implementation of the cy.intercept() method
Fig. 9 Cypress example – implementation of the cy.intercept() method

The intercept implementation, which we subsequently use in the test, is shown above. Its task is to:

  1. Visit google.pl webpage.
  2. Accept cookies.
  3. Enter “Sii” in the search bar and click on the Enter button.
  4. Wait for the @finishedGoogleSearch alias.
  5. Check whether the text “Około” is visible on the loaded page.
Cypress example – test code using the wait for alias method
Fig. 10 Cypress example – test code using the wait for alias method

I advise avoiding waits for a given time and focus on waiting for aliases. It will make our tests shorter and the code will be more optimized and significantly free of unexpected exceptions.

One of the additional packages for the ESLint is the Cypress ESLint Plugin, which allows us to define the rule “cypress/no-unnecessary-waiting”: “error” and detect these waits while creating automated tests.

Custom waits implementation

In case we need a custom wait, that doesn’t qualify for the previously described cy.wait(), we can use a recursive wait with a condition check with every reference.

Recursion is the reference of a function or definition to itself. It occurs in mathematics (Euclid’s algorithm, Fibonacci sequence), programming and even art (a picture within a picture or placing two mirrors opposite each other – that’s exactly when we get a reflection within a reflection).

But let’s get back to programming and our example. We want to write a wait, which will wait for the expected item to appear on the page.

Cypress example – recursive wait implementation for an element to appear
Fig. 11 Cypress example – recursive wait implementation for an element to appear

Wait performs this task in the following order:

  1. Retrieving the contents of the body with the .get() method.
  2. Then, changing this object into a JQuery object, calling the .find(locator) method on it and finally .length, which returns 0 if the element is not in the body.
  3. Waiting in the split time – that is the time after which we want to make another check whether the element meets the given condition.
  4. Defining totalTime and starting to increase it by a given break time (split) with each subsequent recursive call.
  5. Checking if totalTime is less than or equal to the timeout. Depending on the result of the condition, it recursively calls the checkForElement() function, or calls cy.get(resultText) to negate the test.
  6. Finally, calling cy.get(resultsText) when the condition body.find(elementID).length === 0 is not met, which closes the recursion.

It is not a perfect solution of that problem and should be used as a last resort, because we are dealing with a difficulty of code readability and maintainability.

The code above is very hard to debug in Cypress and incompetent use of recursion or loops may result in falling into an infinite loop. endless loop. There is a possibility of its occurrence when mixing synchronous and asynchronous code or when the increment conditions are incorrectly controlled. Eventually, the infinite loop will cause the test error and the browser will stop responding.

External wait packages available

In this chapter, I will go over two packages, that I know of, which we can use in our tests.

  • cypress-wait-until – it allows waiting for everything that the cy.wait() method is not waiting for. The cy.waitUntil() method takes two arguments. The first one is a function that executes a Cypress method, methods or a whole chain of actions. The next argument is an option object, which allows controlling the timeout and interval. The code tries to access the browser field for three seconds with a one second interval. By editing the default values, we can freely control the timeout and interval values. The default values for this wait are timeout=5000, interval=200.
Fig. 12 Cypress – exemplary implementation of cypress-wait-until
Fig. 12 Cypress – exemplary implementation of cypress-wait-until
  • cypress-recurse – the package provides a recursive wait until the expected codition returns a True value. In this case, our expected condition is expect(button).to.have.value(‘Szukaj w Google’)}. The method will negate the test if within 30 seconds this condition will not return the True value. The condition itself will be checked with the frequency of 0.5 seconds. Default values for the method are: timeout=4000, limit=15, delay=800.
Cypress – przykładowa implementacja cypress-recurse
Fig. 13 Cypress – exemplary implementation of cypress-recurse

Selenium vs. Cypress waits

So let’s look for similarities between Selenium waits and Cypress waits.

Let’s start with the implicit wait. We can observe a similar behavior in Cypress – by controlling “defaultCommandTimeout” or the timeout itself, we can wait for an element to appear on the page with the use of the cy.get() method in the time we specify.

Explicit wait in Cypress is quite a controversial topic, because it is supposed to check a condition. Cypress .should(‘be.visible’) method can be treated as the equivalent of the expected condition in Selenium elementIsVisible(element).

However: is the assertion a wait? If we assume that it is, then we can treat all .should() assertions as an explicit wait. I am sure that opinions on this theory are divided, so I leave the answer to this question to your own interpretation 😊

Let’s not forget the cy.wait(@alias). It is this wait that can be considered a full-fledged explicit. The presented cypress-wait-until-until and cypress-recurse external packages are closer to an explicit, or even a fluent wait, due to the possibility to control the interval according to which the condition is checked.

Summary

In this article, I have presented the available ways of waiting for elements in Selenium and Cypress – waits, which allow you to work freely on most elements. I have presented the difference in time with Cypress assertions. I have prepared a sample code with my own wait and have gone over two external packages that we can work with in Cypress, depending on our needs.

If you are starting your adventure with Cypress, it is worth acquiring knowledge of the cy.wait(‘@Alias’) area from the beginning. It is a basic functionality of the tool, which increases the quality of the code in our automated tests and eliminates the need to wait for the element to load. Good luck!

Sources

***

If you are interested in the topic of Cypress, Selenium or test automation, we encourage you to check out other articles by our experts.

4.8/5 ( votes: 44)
Rating:
4.8/5 ( votes: 44)
Author
Avatar
Piotr Flis

His adventure with tests began in 2015 working with Silk Test and Python. Currently working on test automation in Cypress with the use of JavaScript and TypeScript. He is the founder of a sport shooting group Sii-ła Magnum in Sii. After work puts emphasis on personal development in the test automation area and shooting practice. In all likelihood, he can be found at the largest IPSC competitions in Poland, in the Standard Shotgun category.

Leave a comment

Your email address will not be published. Required fields are marked *

You might also like

More articles

Don't miss out

Subscribe to our blog and receive information about the latest posts.

Get an offer

If you have any questions or would like to learn more about our offer, feel free to contact us.

Send your request Send your request

Natalia Competency Center Director

Get an offer

Join Sii

Find the job that's right for you. Check out open positions and apply.

Apply Apply

Paweł Process Owner

Join Sii

SUBMIT

Ta treść jest dostępna tylko w jednej wersji językowej.
Nastąpi przekierowanie do strony głównej.

Czy chcesz opuścić tę stronę?