DEV Community

Cover image for Cypress Best Practices for Test Maintenance
David Auerbach
David Auerbach

Posted on

Cypress Best Practices for Test Maintenance

More and more JavaScript app developers are using Cypress to automate their web applications. Though Cypress is gaining popularity due to its multiple ease of features and speed, developers are also facing certain challenges.

Commonly reported issues that impact test maintenance are poor element locators, test flakiness, environmental fluctuations, and inherent framework limitations.

So, what are the Cypress best practices that enable one to handle Cypress test maintenance if one has already decided to use it as their choice of automation tooling?

1. Use Resilient Selectors

As a testing engineer, your first step to identifying elements in a web page would always be using CSS-based attributes like id, class, or tag or even text that appears on the web page for an element. Issue with using this strategy is being vulnerable and assuming that your tests are brittle owing to any changes made to the web page CSS.

Most effective strategy in Cypress world would be to use custom data-* attributes specifically designed for testing purposes. Consider an example scenario where you want to interact with the Agree button on the webpage, and you use a CSS selector like cy.get('.btn.primary'), which is tightly aligned to the button styling.

This identification might fail if the button is renamed or worse deleted. More resilient approach would be to add a data-cy attribute to the button in the HTML:

Submit
Enter fullscreen mode Exit fullscreen mode

Your Cypress test then tries to access this button using:

cy.get('[data-cy="submit-button"]').click()
Enter fullscreen mode Exit fullscreen mode

This simple change ensures UI updates don't destabilize your test strategy.

2. Avoid Unnecessary Waits

Flaky tests are the nemesis of a good testing strategy. One of the key reasons for flaky tests in Cypress testing strategy is overuse of static waits. When you use the cy.wait(timeout) command, it forces the test to wait for that duration regardless of whether the element is loaded in the DOM or not. Cypress offers a sophisticated approach to using the cy.wait() command by adding assertions like the .should() clause to ensure that the entire wait timeout isn't used.

Consider an example scenario where you are browsing products on a website. You would probably think the best way would be to use the following code that waits 3 seconds even if the product list is completely displayed:

cy.visit('/products');
cy.wait(3000);
cy.get('.product-item').should('have.length.greaterThan', 0);
Enter fullscreen mode Exit fullscreen mode

Cypress best practices for code that would be more efficient would be as follows where Cypress waits for 10 seconds for at least one element with class .product-item to appear on the page:

cy.visit('/products');
cy.get('.product-item', { timeout: 10000 }).should('have.length.greaterThan', 0);
Enter fullscreen mode Exit fullscreen mode

Opting to use Cypress's built-in features instead of static time-based waits, you can create reliable and efficient test cases.

3. Ensure Test Isolation

Creating tests that are self-contained and not dependent on data from previous tests is the most logical approach for creating Cypress test suites. When you use the beforeEach() and afterEach() hook to reset the application state before execution of each test, you ensure that data from previous test execution doesn't trickle into the next one.

Another test isolation technique would be to avoid using UI to set application state. For example, to test features behind a login, you could use cy.request() to call your application's login API and then set the resulting authentication token in a browser cookie or local storage directly, bypassing the entire UI login flow. This ensures that execution of your tests is sped up with low dependence on UI stability.

Test isolation is critical especially if you are using cloud testing platforms such as BrowserStack for cross-browser testing that reuses code for different browsers.

4. Leverage Custom Commands

When you are creating multiple test cases for different scenarios across your web application, there might be pieces of code that repeat across multiple test cases, such as logic for handling user authentication might be something you add in multiple test cases. This means if the logic changes in one place, you will have to ensure that it is changed across test cases to avoid execution failures.

To avoid these issues, Cypress provides custom commands that enable modularity in your test suite. You can define your own commands in the cypress/support/commands.js file to modularize common workflows or sequences of Cypress commands.

Using this approach ensures code reuse and a less verbose and concise code source. Custom commands also accept arguments, making them flexible and reusable. General guidelines would be to use custom commands in situations where code is reused in more than two test cases to extract true benefits.

5. Write Clear and Descriptive Tests

Another critical step that test engineers tend to take lightly is readability of tests. Adding multiple functionalities in single tests, or giving generic names to tests making it difficult to identify what the test caters to, can affect readability.

A common and effective convention when you write test steps is to start your test names with the word "should," followed by a description of the user action or the system behavior being verified, and then the expected result. For example, instead of using a generic name (test1), use a descriptive name like should allow a logged-in user to successfully add a product to their shopping cart.

The Cypress best practices described in this article are some of the practices that require meticulous planning before testing strategy is implemented. Organizational commitment and planning are required to ensure these are followed for Cypress test maintainability.

Well maintained test suite can be a safety net that helps teams be confident about the state of the web application, and also deliver robust application experience to their customers.

Happy to take any questions and discuss more on this!

Top comments (0)