Greetings to all Cypress enthusiasts!
Those who have had experience working with Cypress.io know that it is truly a wonderful tool that provides great opportunities for developing and implementing high-quality and reliable tests in your projects. Therefore, all that remains for us is to be able to make the best use of these opportunities.
The fact is that over the past few years, I have encountered various approaches to writing tests, sometimes with fundamentally different models and templates for building test scenarios and test cases. And it often happened that already written tests needed serious improvement or even complete restructuring, mainly to ensure their reliability and reduce the time it takes to run them. It’s obvious that as a QA engineer, it is crucial to continuously evaluate and optimize testing processes to ensure the highest possible level of quality assurance.
And in my observations, problems were often caused not only by a lack of deep understanding of the built-in commands of the testing framework, but also very often by an inability to apply basic programming language capabilities in tests. This has prompted me to write a series of articles in which I share some of my techniques for optimizing Cypress tests, drawing on simple and mostly familiar JavaScript capabilities.
Among the most common errors, a separate group can be identified for cases where QA engineers repeatedly perform exactly the same actions on different elements, be it form fields, buttons, links, or any other UI elements. Often, depending on the tested elements or actions performed, they are often divided into separate tests. This leads to a sharp increase in the number of tests, which is further compounded by the presence of multiple prerequisites placed in beforeEach and afterEach hooks, which are repeated over and over again as test suites are executed. In some cases, such unjustified omissions ultimately lead to a significant increase in the overall time it takes to run tests.
In general, it is important to note that such erroneous actions do not correspond to the DRY principle, which can make the tests harder to maintain, slower to run, and more prone to errors and inconsistencies.
Therefore, in the first article, I would like to discuss when and how the best way to optimize repeatedly performed actions, events, or assertions in Cypress tests using basic JavaScript loops to avoid the above-mentioned ungrateful situations.
After analyzing the most frequently occurring cases of iterative actions during testing, I identified 8 common scenarios where the use of loops is most effective. This is certainly not an exhaustive list, as other cases may arise depending on the specified test scenarios. Next, I plan to examine each of these scenarios in detail and demonstrate with specific examples how loops can significantly help optimize your Cypress tests.
1. Iterating through a set of items or elements
When iterating through a set of items or elements, loops come in handy to perform repetitive tasks. For instance, if there are multiple checkboxes on a web page, a loop can be used to select or deselect each one of them. The loop can be controlled by setting the start and end values, making the code cleaner and efficient. By using loops in this scenario, you can ensure that all items or elements are verified or interacted with as needed.
In this example, we use the Array.from()
method to convert an array-like object returned by Cypress cy.get()
command to a standard array. We then use a forEach()
loop to iterate over each element and perform some action.
2. Repeating a test with different inputs or expected outputs
For example, if there are multiple forms on a page, each requiring different inputs, a loop can be used to run the same test with each form. The loop can be controlled using conditional statements that verify the expected outputs for each input. This approach saves time and reduces the amount of code needed to test multiple scenarios.
In this example, we use a for…of loop with destructuring to repeat a test with different inputs and expected outputs. We define an array of objects, each with an input and expected output property, and use the loop to iterate over each object and perform the test.
3. Navigating through multiple pages
Cypress test can navigate through multiple pages of a web application by implementing a loop to check the contents of each page. The loop’s value can be regulated with conditional statements that confirm the existence of a next or previous page. Such a technique not only enhances the code’s organization but also improves its readability.
This example demonstrates how to use forEach()
loop to navigate through multiple pages. The code creates an array of page URLs, and then uses a forEach()
loop to visit each page URL and assert that the page has loaded correctly using Cypress’s should()
method.
4. Testing multiple user accounts or roles
If there are multiple roles or user accounts in a web application, a loop can be used to test each role or account by logging in with each set of credentials. The loop can be managed through conditional statements that confirm the anticipated results for every user role or account.
The value of using the for…of loop in this Cypress test is to execute the same test scenario multiple times with different sets of data. In this case, the test scenario is logging in with a specific user's credentials, performing some actions specific to their role, and then logging out. The loop iterates over the users array and performs the test scenario for each user in the array. This saves time and effort in writing individual tests for each user account or role, and allows for more efficient and comprehensive testing of the system under different user scenarios.
5. Running the same test with different configurations
If a web application has multiple configurations, a loop can be used to test the application with each one. The loop can be determined by implementing conditional statements that define the anticipated results for every configuration.
The value of using the for…of loop in this test is to optimize the test execution by reducing code duplication. By iterating over the configs
array and using the configuration data to set up the testing environment and execute the tests, we can avoid repeating the same setup and test code for each configuration.
This not only makes the test code more concise and easier to read, but it also reduces the risk of errors and omissions that can occur when copying and pasting test code. Additionally, by separating the configuration data from the test code, we can easily add or remove configurations without having to modify the test code.
6. Dynamically generating test data
Loops can be used to generate test data dynamically. For instance, if there is a need to create multiple user accounts, a loop can be used to generate a set of user data with each iteration. The loop can be controlled by setting the start and end values and by specifying the number of users to create.
The forEach()
loop is used here to iterate over each key in the testData
object and perform a test for each input. This loop enables the code to avoid duplicating the same test case for each input, and instead, it performs the same test for each input dynamically, thus saving time and effort.
By using a loop to iterate over the testData
object, it becomes easy to add more test cases without duplicating code. This makes the code more efficient and reduces the likelihood of errors when adding new tests.
7. Iterating through a set of test steps
Also, loops can be used to iterate through a set of test steps when testing a web application. For example, if there are multiple steps involved in testing an application, a loop can be used to repeat the steps with each iteration. The loop can be managed using conditional statements that specify the expected outputs for each step.
In this Cypress test each step is an object that contains information about an action that needs to be performed during the test, such as clicking on a button or typing into an input field. By using a loop forEach()
to iterate through the steps array, the test can perform each action sequentially and with less duplicated code.
Inside the loop, the test checks the action property of each step object to determine what action should be performed. Depending on the value of action, the test will use a different Cypress command to perform the action. For example, if the action is click, the test will use the cy.get().click()
command to simulate a button click.
8. Testing the same functionality or behavior across different environments
Loops can be used to test the same functionality or behavior across different environments, such as multiple screen resolutions or browser types. For example, if a web application needs to be tested on different browsers or screen resolutions, a loop can be used to test the application on each configuration. The loop can be controlled using conditional statements that verify the expected outputs for each environment.
Inside the loop, the test sets the viewport size using the cy.viewport()
command to match the width and height properties of the current environment. Using a loop in this way allows the test to be run multiple times with different configurations, without having to duplicate the same test code. This can make the test more efficient and easier to maintain, especially when there are many different environments to test against.
Conclusion
Thus, we have confirmed that there are numerous useful applications of loops in Cypress tests, significantly improving testing efficiency, reducing the time spent on test runs, and enhancing test coverage. Overall, loops are an important tool for automating testing in Cypress, improving the process and saving time and effort. In our next articles, we will explore other JavaScript features that can help optimize your Cypress tests.
The source code of all presented examples can be found in the corresponding repository on GitHub.
Thank you for your attention! Happy testing!
Top comments (8)
Excellent article, appreciate
Very useful, thanks!
Really? I thought using traditional for/forEach loops with the async nature of Cypress was a really bad idea (?) Also, if you use for/forEach around your it() blocks, those it blocks won't have any before/beforeEach code run before them.
Thank you for your comment. However, no one denies the asynchronous nature of Cypress commands. It seems to me a bad idea to deny something in general, generalizing everything under one general prohibition, without taking into account the specifics of a particular situation. however, what prevents you from optimizing the test code by placing repeated commands for various elements inside the loop? the simplest example with checking the existence of a set of elements on the page. Why write Cypress commands for each element when you can loop through the entire list of elements?
Would be good to see some examples of where forEach is a bad idea. Reason being is that people will read this article, add a
beforeEach
to their tests and wonder why it isn't working ... then blame Cypress for being flakey :)sorry, but wait, this article is not about test suite structure. Using loops does not oblige you to put "it" blocks inside the loop. These are schematic, simple examples, demonstrating only the general principle of possible use cases for loops. in those special cases where you saw "it" inside the loop, nothing prevents you from putting "it" inside the loop, placing preconditions in the test body, putting a whole describe block inside the loop, and so on. It depends on the specific situation. I think this is the basic knowledge of building tests that a tester should have to work with automation tools.
All that for just asking a question.
however, you expressed your opinion about what I wrote. I just tried to share with you my point of view. if that was too much i'm sorry. I was just trying to be more clear. in any case, thanks for your attention to my article.
Some comments have been hidden by the post's author - find out more