Embrace Cypress Best Practices for Effective Test Automation in 2024

Cypress automation services

In today’s competitive business landscape, performing functional tests is vital for software’s success. It enables the developer to validate the software’s behavior. Developers like Cypress require the correct technology to improve their efficiency and productivity. However, leveraging Cypress to its maximum capability requires proper implementation. That is only possible through a thorough understanding of industry best practices.

Adopting these practices will help your project yield significant benefits. They will also ensure you avoid common mistakes that slow down your progress. Moreover, they empower teams to mitigate risks. Thus, these practices ensure seamless operation and safeguard against costly failures. Join us as we uncover the proven methodology to supercharge your testing endeavors. These tried and tested practices will pave the path for innovation by eliminating the bottlenecks.

Cypress Best Practices For Test Automation

Utilizing Custom Data Attributes

Consider using data attributes when selecting elements for your tests. It is one of the best Cypress best practices to ensure your test remains robust. Using custom data attributes like data-cy or data-test-id, you can create selectors explicitly meant for testing purposes because selecting elements using id and class can be unreliable. They are often used for styling and behavior, making them prone to changes. Therefore, using data attributes designed specially for testing ensures more stable tests. This approach reduces the risk of brittle tests caused by changes in styling or behavior.

For example:



cy.get('button'); // Too generic

cy.get('.button'); // Tied to CSS

cy.get('#button'); // Tied to JavaScript

cy.get('[type="submit"]'); // Tied to HTML

Implementing Network Traffic Control for Edge Case Testing

Mobile network conditions can play a significant role in its proper functioning. Therefore, testers can implement network traffic control for edge case test automation. It allows them to evaluate the functioning of their application under adverse network conditions. Thus, by mimicking real-world scenarios, testers can ensure consistent functionality. However, while performing edge cases, checking situations that might only happen sometimes in real life is essential.

Sometimes, Edge cases can occur due to interactions between APIs. Cypress testing has a feature called .intercept() that helps manage these pretend situations. While .intercept() can handle these edge cases, it must be used carefully.

describe('Edge Case Testing with Network Intercept', () => {

it('should handle 500 Internal Server Error gracefully', () => {

// Intercept POST requests to /api/data and simulate a 500 server error response

cy.intercept('POST', '/api/data', {

statuscode: 500,

body: { error: 'Internal Server Error' },


// Initiate the action that prompts the Cypress automation services to send a request to /api/data.

// For example, submitting a form in your application


// Wait for the intercept to occur


// Assert that the application shows an appropriate error message to the user

cy.get('.error-message').should('contain', 'An unexpected error occurred');



Scenarios Benefiting from Real-Time Execution and Command Log

Real-time execution and command log features are handy when fixing complicated website problems. They’re instrumental on websites that change frequently or have different steps or situations. Real-time execution allows you to see test actions as they happen.

Consequently, it facilitates the developers’ ability to find and fix problems. Further, real-time execution can also help pinpoint the root cause of the error. Developers must ensure the command log is enabled in their Cypress automation tool during the test execution.

Another critical factor is to utilize assertions, as this will ensure that the specific conditions are met during the testing; developers can use a built-in assertion library or create a custom assertion.

Control State Programmatically

When testing, set the application state programmatically whenever possible instead of relying on the UI. This decouples the UI from the state and improves performance since the programmatic state setting is faster than the UI.

For example:


```cy.request('POST', '/login', { email: '[email protected]', pass: 'testPass' });```


```cy.get('[data-cy="email"]').type('[email protected]');```

```cy.get('[data-cy="pass"]').type('[email protected]');```


To communicate directly with the API, use `cy.request` rather than the UI, as in the second code sample. Leading vendors of Cypress testing solutions also prefer this method of incorporating test data into the application to determine the intended state.

Cypress basics: before(), beforeEach(), after() and afterEach()

In Cypress, four hooks allow you to execute standard code in a spec file before or after each test case or before/after all test cases. Using these hooks, you can avoid repeating the same code at the beginning or end of each test case, making your test suite more organized and easier to maintain.

1. beforeEach(): This hook runs before each test case in a specification file. You can place the standard code that needs to run before every test here.

2. afterEach(): The afterEach() hook executes after each test case in a spec file. Running cleanup code or assertions that need to be verified after each test is helpful to Cypress testing companies.

3. before(): The before() hook runs once before each test case in a specification file. It’s suitable for setting up common preconditions or configurations that apply to all tests in the file.

4. after(): This hook runs once all test cases in a spec file have been completed. It’s handy for cleanup tasks or for performing actions that should occur after all tests have finished running.

Avoid Single Assertions

It’s advisable to avoid using single assertions in Cypress E2E tests. Although single assertions may suffice for unit tests, they might not be optimal for E2E testing. Consolidating multiple assertions within a single test case can help an automation testing company pinpoint the exact failed assertion.

For Example:



it('Should have an external link pointing to the right domain', () => {


.should('have.length', 1)


.should('contain', '')

.and('have.attr', 'target', '_blank');





it('Should have a link', () => {


.should('have.length', 1)



it('Should contain the right text', () => {

cy.get('.link').find('a').should('contain', '');


it('Should be external', () => {

cy.get('.link').find('a').should('have.attr', 'target', '_blank');



Cypress runs lifecycle events between tests, resetting the state, which can impact performance when using single assertions. Hence, a test automation company prefers dividing assertions into multiple test phases to maintain an effective test suite.

Using dynamic wait

Instead of using a static wait command like `cy.wait(timeout)`, which can lead to unnecessary waiting times, it’s recommended to use dynamic waits with `cy.intercept()` for better efficiency.

For example:


cy.intercept('POST', '**/login').as('login');




In this code snippet, we’re using `cy.wait()` to pause execution until the specific API endpoint “login” returns a response. This approach ensures that the script proceeds only after receiving the desired API result, thereby reducing waiting time and improving script execution speed.

Independent it() blocks

When writing test cases in a spec file, it’s crucial to ensure that each `it()` block operates independently of the others. They must not rely on each other’s outcomes. This coding principle ensures that even if one test case fails during execution, it won’t impact the execution or outcome of the other test cases. Thus, by using best practices in coding, you can perform more reliable and isolated testing.

For example:


describe('Example test suite', () => {

it('Test case 1 should...', () => {

// Test case script for the first scenario


it('Test case 2 should...', () => {

// Test case script for the second scenario


// Additional it() blocks for other test cases



Each `it()` block represents a standalone test case, ensuring that failures in one test case do not affect the execution of others. This approach enhances the test suite’s robustness.

Adding BaseUrl in the config file

One best practice is to define the base URL in the `cypress.json` configuration file. It will automatically prepend this base URL to any relative paths specified in `cy.visit()`. This ensures that the correct URL is visited without needing hardcoded URLs in each spec file, resulting in more efficient and professional test execution.

Here’s how you can do it:

1. Open your `cypress.json` file.

2. Add the following line specifying the base URL:



"baseUrl": "http://localhost:3000"



3. In your spec files, use `cy.visit()` with the relative path:


before(() => {





Mastering Cypress is not just about writing efficient test automation code but also about implementing these best practices. That fosters a culture of quality and innovation in the software development lifecycle. Businesses can leverage these proven methodologies to unlock new efficiency and agility levels in their testing processes. As we navigate the fast-changing software development landscape, Cypress remains a steadfast ally. It empowers teams to deliver seamless digital experiences that delight users experience to drive business success. Undoubtedly, Cypress continues to lead the way in test automation, shaping the next chapter of software testing in 2024 and beyond.

The following two tabs change content below.


Co-Founder & Director, Business Management
AutomationQA is a leading automation research company. We believe in sharing knowledge and increasing awareness, and to contribute to this cause, we try to include all the latest changes, news, and fresh content from the automation world into our blogs.