# Cypress Best Practices: Test Automation Guide for 2026

Written by Dominik Szahidewicz
Published: 2026-06-08
Updated: 2026-06-09

\[TABLE\_OF\_CONTENTS\]

Selecting the right tool and adhering to best practices is paramount for achieving efficient and effective testing processes. One tool that has gained significant popularity among developers and quality assurance professionals is **Cypress**.

In this comprehensive guide, we will delve into the service, explore its advantages and disadvantages, and outline best practices for harnessing its full potential in your test automation endeavors.

![Cypress Best Practices](https://bugbug-homepage.s3.eu-central-1.amazonaws.com/let_me_introduce_66913b919c_842c5be422.jpeg)

`<custom-yellow-tip><json>{"name":"yellow-tip","children":[{"name":"yellow-tip-title","children":[{"name":"paragraph","children":[{"attributes":{"bold":true},"data":"Check also: "}]},{"name":"paragraph"}]},{"name":"yellow-tip-details","children":[{"name":"paragraph","children":[{"data":"👉 "},{"attributes":{"linkHref":"https://bugbug.io/blog/software-testing/web-testing-tools/"},"data":"Best Web Testing Tools"}]},{"name":"paragraph","children":[{"data":"👉 "},{"attributes":{"linkHref":"https://bugbug.io/blog/test-automation-tools/open-source-test-automation-tools/"},"data":"Open-source Automation Tools "}]},{"name":"paragraph","children":[{"data":"👉 "},{"attributes":{"linkHref":"https://bugbug.io/blog/test-automation-tools/record-and-play-automation-tools/"},"data":"Record and Playback Tools "}]},{"name":"paragraph"}]}]}</json>  <yellow-tip-title-md>  **Check also:**   </yellow-tip-title-md>  <yellow-tip-details-md>  👉 [Best Web Testing Tools](https://bugbug.io/blog/software-testing/web-testing-tools/)  👉 [Open-source Automation Tools](https://bugbug.io/blog/test-automation-tools/open-source-test-automation-tools/)   👉 [Record and Playback Tools](https://bugbug.io/blog/test-automation-tools/record-and-play-automation-tools/)   </yellow-tip-details-md>  </custom-yellow-tip>`

## Cypress Best Practices - Basics

**Cypress** is an open-source JavaScript [e2e testing](https://bugbug.io/blog/software-testing/what-is-end-to-end-testing/) framework designed for modern web applications. The automation tool operates directly within the browser, allowing developers to write test cases with real-time feedback.

It provides a robust set of capabilities for performing tasks such as interacting with web elements, simulating user actions, and making assertions about the application's behavior. After local installation you can perform one of the common testing scenarios: checking login page functionality. 

`<custom-purple-tip><json>{"name":"purple-tip","children":[{"name":"purple-tip-title","children":[{"name":"paragraph","children":[{"attributes":{"bold":true},"data":"💡 Check also our "},{"attributes":{"bold":true,"linkHref":"https://bugbug.io/blog/software-testing/cypress-cheat-sheet/"},"data":"Cypress Cheat Sheet"},{"attributes":{"bold":true},"data":"."}]},{"name":"paragraph"}]},{"name":"purple-tip-details","children":[{"name":"paragraph"},{"name":"paragraph"}]}]}</json>  <purple-tip-title-md>  **💡 Check also our** [**Cypress Cheat Sheet**](https://bugbug.io/blog/software-testing/cypress-cheat-sheet/)**.**  </purple-tip-title-md>  <purple-tip-details-md>  </purple-tip-details-md>  </custom-purple-tip>`

Cypress is a cutting-edge front-end testing tool that has rapidly gained popularity for its ability to streamline and enhance the process of conducting comprehensive tests on web applications.

It provides a robust environment for creating and running testing suites, making it an indispensable tool for modern software development teams. Here, we'll delve into the world of Cypress and explore key concepts and best practices associated with it.

**\- Test Suites and Test Cases:**

Cypress enables developers to organize their testing efforts effectively through the use of test suites and test cases. A test suite refers to a collection of related test cases, and each test case is an individual test. Cypress excels at running both unit testing and end-to-end (E2E) testing scenarios.

**\- Running Tests:**

Using Cypress, you can execute tests in a controlled manner. It provides a user-friendly Cypress runner that allows you to initiate and manage test runs with ease. You can interact with the test cases using the UI, overseeing the progress of every test case.

**\- Defining Tests:**

Defining a test in Cypress is straightforward. You can use the it function to specify each test case within your test code. These tests are typically organized into separate test specs to ensure isolation and clarity in your testing efforts.

**\- Cypress Cloud:**

Cypress offers a cloud-based solution called Cypress Cloud, which facilitates running and managing tests in a distributed environment. This is particularly useful when you have a large number of test cases, such as 10 test cases or more, and you want to leverage the power of cloud resources to speed up testing.

**\- Test Data and Assertions:**

When writing tests in Cypress, it's important to include test data and assertions to guard Cypress from proceeding until an explicit condition is met. This helps ensure that your tests accurately reflect the state of your application and produce reliable results.

`<custom-cta-card><json>{"name":"cta-card","children":[{"name":"cta-card-title","children":[{"name":"paragraph","children":[{"attributes":{"bold":true},"data":"Automate your tests for free"}]},{"name":"paragraph"}]},{"name":"cta-card-details","children":[{"name":"paragraph","children":[{"data":"Test easier than ever with BugBug test recorder. Faster than coding. Free forever."}]},{"name":"paragraph"}]},{"name":"cta-card-cta","children":[{"name":"paragraph","children":[{"attributes":{"bold":true},"data":"Get started"}]},{"name":"paragraph"}]}]}</json>  <cta-card-title-md>  **Automate your tests for free**  </cta-card-title-md>  <cta-card-details-md>  Test easier than ever with BugBug test recorder. Faster than coding. Free forever.  </cta-card-details-md>  <cta-card-cta-md>  **Get started**  </cta-card-cta-md>  </custom-cta-card>`

## Advantages of Using Cypress

**1\. Real-Time Debugging**

Cypress offers real-time debugging capabilities, enabling developers to pause test execution, inspect the application's state, and troubleshoot issues directly from the browser. This feature greatly expedites the debugging process and enhances productivity.

**2\. Automatic Waiting**

One of Cypress's standout features is its automatic waiting. It intelligently waits for elements to become interactive or visible before executing actions, eliminating the need for manual timeouts and ensuring test stability.

**3\. Interactive Test Runner**

Cypress provides an interactive test runner that displays the application in one pane and the test's commands and assertions in another. This split-screen view facilitates real-time validation and aids in identifying issues quickly.

**4\. Consistent and Reliable Tests**

With Cypress, you can write consistent and reliable tests due to its automatic retries and assertions. Tests automatically retry failed commands, reducing false negatives and improving test stability.

**5\. Seamless Integrations**

Cypress seamlessly integrates with various Continuous Integration (CI) tools, making it easy to incorporate automated tests into your CI/CD pipeline. It supports platforms like Travis CI, CircleCI, Jenkins, Github and more.

## Disadvantages of Using Cypress

While Cypress offers numerous advantages, it's essential to consider its limitations:

**1\. Limited Browser Support**

Cypress primarily supports Chromium-based browsers, which may limit its compatibility with applications that require testing across a wide range of browsers.

**2\. No Cross-Domain Testing**

Due to browser security restrictions, Cypress cannot test applications across different domains in a single test. This limitation can be challenging when working with complex web ecosystems.

**3\. No Support for Native Mobile Apps**

Cypress is focused on web application testing and does not support testing of native mobile applications, unlike some other testing tools.

## Cypress Best Practices You Need for Testing

### 1\. Stay Organized!

Organizing your tests is crucial for maintaining a scalable and maintainable test suite. Avoid sharing page objects, as this can lead to code duplication and brittle tests. Instead, write test specs in isolation, ensuring that every test case is self-contained and does not rely on the state left by previous tests.

Additionally, avoid using the application's UI to log in during tests. Instead, programmatically log into your application using API calls or other methods. This approach ensures that your tests are not dependent on the login process and can run independently. 

### 2\. Run Tests Intelligently

As your test suite expands and its execution time increases, you might encounter issues in the CI system. You should integrate your version control system with your test suite in a way that prevents merges until all your tests have successfully passed. However, it's important to acknowledge that this approach can result in longer test execution times, which can slow down the process of merging branches and delivering new features. This issue can become even more pronounced when you have chains of dependent branches awaiting merging.

One effective solution to mitigate this problem is to implement **Smart Orchestration** through Cypress Cloud. Smart Orchestration leverages a combination of parallelization, load balancing, Auto Cancellation, and Spec Prioritization to optimize your available computing resources and minimize resource wastage.

This approach enables you to maintain a high level of efficiency in your testing processes while accommodating the scaling demands of a growing test suite.

### 3\. Select Elements with Data-\* Attributes

When selecting elements for your tests, avoid using selectors based on CSS attributes such as id, class, or tag, as they are prone to changes. Instead, use `data-*` attributes to provide context to your selectors. Elements with data-\* attributes are more stable and less likely to break when the application's styling or behavior changes.

### 4\. Don't Start Your Back End Web Server From Within Cypress

Every command executed through `cy.exec()` or `cy.task()` must eventually terminate; otherwise, Cypress will be unable to proceed with any other commands.

Attempting to launch a web server using `cy.exec()` or `cy.task()` introduces a range of complications due to the following reasons:

1.  You must background the process.
2.  You lose the ability to interact with it through the terminal.
3.  Access to its standard output (stdout) or logs becomes unavailable.
4.  You need to handle the intricacies of starting an already running web server every time your tests run.
5.  Frequent port conflicts are likely to arise.

As for shutting down the process in an after hook, it's not a reliable solution because there is no assurance that code within an after hook will always execute. In the Test Runner, you can restart or refresh the environment during a test, which can prevent the code in an after hook from running when such interruptions occur.

### 5\. Use Cypress Testing Library

Cypress works well with the Testing Library project, which promotes writing tests that resemble how users interact with your application. You can use library package to access familiar testing library methods for selecting elements in Cypress specs. This aligns with best practices for user-centered testing.

### 6\. Use Aliases and Closures to Access and Store what Commands Yield You.

Cypress commands are asynchronous and do not return values that you can assign with const, let, or var. Instead, use aliases and closures to access and store the values or elements returned by Cypress commands. This ensures that you work with the correct values during test execution.

### 7\. Only Test Websites That You Control

Use `cy.request()` to communicate with 3rd-party servers via their APIs, if necessary.

When you need to run a test, focus on testing websites and servers that you control. Avoid attempting to interact with or test third-party websites or services, as this can lead to various issues, including time-consuming tests and potential blocks or bans.

If you must interact with third-party services, consider using cy.request() to communicate with their APIs programmatically. Cache results using `cy.session()` to avoid repeat visits and improve test performance.

### 8\. Run Independent Tests

It's essential to write tests that are independent of each other and can be run in any order. Avoid coupling multiple tests together, as this can lead to unreliable and unpredictable test results.

To achieve this, use `before` or `beforeEach` hooks to set up the initial state for each test. This ensures that tests start with a clean slate and do not depend on the state left by previous tests.

### 9\. Run Small Tests with a Single Assertion

Cypress stands apart from conventional unit testing by executing a sequence of asynchronous lifecycle events that effectively reset the testing environment between each test case. Consequently, employing individual assertions within separate tests can significantly slow down test execution and severely impact performance.

Opting for multiple assertions within a single test is a more efficient approach compared to creating distinct tests. Therefore, feel confident about incorporating multiple assertions within a single test. Each test run is saved into Cypress Cloud App. 

### 10\. Use Data Attributes When Selecting Elements

One of the foundational principles of effective E2E testing is selecting elements in a way that's resilient to changes in your application's codebase. To achieve this, prefer using custom data attributes like `data-cy` or `data-testid` when identifying elements for testing. These attributes are explicitly designed for testing purposes and are less likely to change due to styling or behavior updates.

### 11\. Independent it() Blocks

When writing test cases in Cypress, it's essential to ensure that `each it()` block is independent of others. Avoid dependencies between test cases, as this allows for better test isolation. If one test fails, it won't impact the execution of other tests.

This approach ensures that the test suite remains stable and reliable even as it grows in complexity.

### 12\. Use Dynamic Waiting

Avoid static waits, such as `cy.wait(timeout)`, when waiting for page actions like loading or API responses. Static waits can lead to unnecessary delays and brittle tests. Instead, consider using `cy.intercept()` to wait for specific API responses before proceeding with your test.

javascript

```javascript
cy.intercept('POST', '**/login').as('login'); cy.visit("/") cy.wait('@login')
```

Dynamic waits speed up script execution and make your tests more efficient and reliable.

### 13\. Leverage `before()`, `beforeEach()`, `after()`, and `afterEach()`

To eliminate repetitive code at the beginning or end of each test case, utilize Cypress hooks like `before()`, `beforeEach()`, `after()`, and `afterEach()`. These hooks allow you to execute common setup and teardown code for multiple test cases.

For example, you can set up a common base URL for all your tests in the before() hook, reducing redundancy and improving maintainability.

### 14\. Adding BaseUrl in the Config File

Hardcoding the base URL in each spec file's `before()` block can lead to inefficiencies and inconsistencies. Instead, define the base URL in the `cypress.json` configuration file to ensure that all spec files use the same base URL. This practice streamlines your test setup and makes it more professional.

### 15\. Define Scripts in package.json

To simplify the execution of Cypress commands, define them in the scripts section of your project's `package.json` file. This allows you to run commands using a user-defined name, enhancing the clarity and ease of running tests.

### 16\. Harness Cypress's Unique Features

Cypress offers several unique features that set it apart from other testing tools:

**\- Time Travel:** Cypress captures snapshots of your application during test execution, allowing you to rewind to any point in your test script. This feature simplifies debugging and error identification.

**\- Retry-ability:** Cypress's automatic retry mechanism ensures that tests proceed as soon as assertions pass, reducing the need for hard-coded waits. This feature is especially useful for testing dynamic web applications.

**\- Effective Element Selection:** Cypress's `cy.get()` method efficiently locates web elements without explicit waits, making tests faster and more reliable.

**\- Spies, Stubs, and Clocks:** Cypress provides tools to verify and control functions, server responses, and timers, offering the flexibility and control typically associated with unit testing.

### 17\. Run Cypress Tests in Parallel

To expedite test execution and achieve faster feedback, consider running Cypress tests in parallel using cloud infrastructure services like BrowserStack. Parallelization can significantly reduce the time it takes to complete your test suite, allowing for quicker integration and deployment cycles.

## Conclusion

By following these [Cypress](https://www.cypress.io/) best practices, you can create robust and efficient E2E tests that ensure your web application performs flawlessly. While implementing some of these practices may initially seem challenging, the long-term benefits of improved test stability and faster execution are well worth the effort.

Cypress has revolutionized test automation with its unique features, and when used effectively, it can be a game-changer for your testing efforts.

Happy (automated) testing!

## FAQ - Cypress Best Practices

``<custom-faq><json>{"name":"faq","children":[{"name":"faq-question","children":[{"name":"heading2","children":[{"attributes":{"bold":true},"data":"How do I stop tests from depending on each other?"}]},{"name":"paragraph"}]},{"name":"faq-answer","children":[{"name":"paragraph"},{"name":"paragraph","children":[{"data":"Use "},{"attributes":{"code":true},"data":"beforeEach()"},{"data":" to reset state before every test rather than relying on state left by a previous "},{"attributes":{"code":true},"data":"it()"},{"data":" block. Each test should be able to run in isolation and in any order. If a test requires a logged-in user, handle login programmatically via API call in the hook — not by clicking through the UI in a prior test."}]}]}]}</json>  <faq-question-md>  ### **How do I stop tests from depending on each other?**  </faq-question-md>  <faq-answer-md>  Use `beforeEach()` to reset state before every test rather than relying on state left by a previous `it()` block. Each test should be able to run in isolation and in any order. If a test requires a logged-in user, handle login programmatically via API call in the hook — not by clicking through the UI in a prior test.  </faq-answer-md>  </custom-faq>``

``<custom-faq><json>{"name":"faq","children":[{"name":"faq-question","children":[{"name":"heading2","children":[{"attributes":{"bold":true},"data":"Should I write one assertion per test or multiple?"}]},{"name":"paragraph"}]},{"name":"faq-answer","children":[{"name":"paragraph","children":[{"data":"Multiple assertions per test is the right call in Cypress. Unlike unit testing frameworks, Cypress resets the environment between each "},{"attributes":{"code":true},"data":"it()"},{"data":" block — which means a single assertion per test multiplies setup and teardown overhead significantly. Group related assertions in one test to keep suites fast."}]},{"name":"paragraph"}]}]}</json>  <faq-question-md>  ### **Should I write one assertion per test or multiple?**  </faq-question-md>  <faq-answer-md>  Multiple assertions per test is the right call in Cypress. Unlike unit testing frameworks, Cypress resets the environment between each `it()` block — which means a single assertion per test multiplies setup and teardown overhead significantly. Group related assertions in one test to keep suites fast.  </faq-answer-md>  </custom-faq>``

``<custom-faq><json>{"name":"faq","children":[{"name":"faq-question","children":[{"name":"heading2","children":[{"attributes":{"bold":true},"data":"How do I speed up a Cypress test suite that's slowing down CI?"}]},{"name":"paragraph"}]},{"name":"faq-answer","children":[{"name":"paragraph","children":[{"data":"Two approaches that compound well together: run tests in parallel using Cypress Cloud's Smart Orchestration, and enable Auto Cancellation so the suite stops early when a critical test fails rather than running to completion. For selector performance, make sure tests use "},{"attributes":{"code":true},"data":"data-*"},{"data":" attributes — unstable selectors that retry repeatedly are a silent performance drain."}]},{"name":"paragraph"}]}]}</json>  <faq-question-md>  ### **How do I speed up a Cypress test suite that's slowing down CI?**  </faq-question-md>  <faq-answer-md>  Two approaches that compound well together: run tests in parallel using Cypress Cloud's Smart Orchestration, and enable Auto Cancellation so the suite stops early when a critical test fails rather than running to completion. For selector performance, make sure tests use `data-*` attributes — unstable selectors that retry repeatedly are a silent performance drain.  </faq-answer-md>  </custom-faq>``

`<custom-faq><json>{"name":"faq","children":[{"name":"faq-question","children":[{"name":"heading2","children":[{"attributes":{"bold":true},"data":"Does Cypress support Firefox and Safari?"}]},{"name":"paragraph"}]},{"name":"faq-answer","children":[{"name":"paragraph","children":[{"data":"Cypress supports Chrome, Firefox, and Edge. It does not support Safari. If Safari coverage is a hard requirement for your test suite, you'll need to supplement Cypress with a cross-browser cloud platform like BrowserStack or LambdaTest, or consider Playwright which supports WebKit natively."}]},{"name":"paragraph"}]}]}</json>  <faq-question-md>  ### **Does Cypress support Firefox and Safari?**  </faq-question-md>  <faq-answer-md>  Cypress supports Chrome, Firefox, and Edge. It does not support Safari. If Safari coverage is a hard requirement for your test suite, you'll need to supplement Cypress with a cross-browser cloud platform like BrowserStack or LambdaTest, or consider Playwright which supports WebKit natively.  </faq-answer-md>  </custom-faq>`

``<custom-faq><json>{"name":"faq","children":[{"name":"faq-question","children":[{"name":"heading3","children":[{"attributes":{"bold":true},"data":"What's the fastest way to handle login in Cypress without slowing tests down?"}]},{"name":"paragraph"}]},{"name":"faq-answer","children":[{"name":"paragraph","children":[{"data":"Skip the UI entirely. Use "},{"attributes":{"code":true},"data":"cy.request()"},{"data":" to call your authentication API directly, then set the resulting session token. This is significantly faster than recording a full login flow for every test and avoids brittleness if the login UI changes. Use "},{"attributes":{"code":true},"data":"cy.session()"},{"data":" to cache and reuse the authenticated state across tests."}]},{"name":"paragraph"}]}]}</json>  <faq-question-md>  #### **What's the fastest way to handle login in Cypress without slowing tests down?**  </faq-question-md>  <faq-answer-md>  Skip the UI entirely. Use `cy.request()` to call your authentication API directly, then set the resulting session token. This is significantly faster than recording a full login flow for every test and avoids brittleness if the login UI changes. Use `cy.session()` to cache and reuse the authenticated state across tests.  </faq-answer-md>  </custom-faq>``

``<custom-faq><json>{"name":"faq","children":[{"name":"faq-question","children":[{"name":"heading2","children":[{"attributes":{"bold":true},"data":"Can Cypress test across multiple domains in a single test?"}]},{"name":"paragraph"}]},{"name":"faq-answer","children":[{"name":"paragraph","children":[{"data":"Not natively. Browser security restrictions prevent Cypress from switching domains within a single test. If your flow requires interaction with a third-party domain — OAuth login, payment provider, analytics service — use "},{"attributes":{"code":true},"data":"cy.request()"},{"data":" to handle the API interaction programmatically rather than navigating to the external domain."}]},{"name":"paragraph"}]}]}</json>  <faq-question-md>  ### **Can Cypress test across multiple domains in a single test?**  </faq-question-md>  <faq-answer-md>  Not natively. Browser security restrictions prevent Cypress from switching domains within a single test. If your flow requires interaction with a third-party domain — OAuth login, payment provider, analytics service — use `cy.request()` to handle the API interaction programmatically rather than navigating to the external domain.  </faq-answer-md>  </custom-faq>``

``<custom-faq><json>{"name":"faq","children":[{"name":"faq-question","children":[{"name":"heading2","children":[{"attributes":{"bold":true},"data":"What's the most common reason Cypress tests break after a UI change?"}]},{"name":"paragraph"}]},{"name":"faq-answer","children":[{"name":"paragraph","children":[{"data":"Selectors tied to CSS classes, IDs, or tag names. These change constantly as developers refactor styling or restructure components. The fix is to use "},{"attributes":{"code":true},"data":"data-cy"},{"data":" or "},{"attributes":{"code":true},"data":"data-testid"},{"data":" attributes specifically for testing — they're invisible to users, ignored by styling changes, and signal to every developer that removing them will break tests."}]},{"name":"paragraph"}]}]}</json>  <faq-question-md>  ### **What's the most common reason Cypress tests break after a UI change?**  </faq-question-md>  <faq-answer-md>  Selectors tied to CSS classes, IDs, or tag names. These change constantly as developers refactor styling or restructure components. The fix is to use `data-cy` or `data-testid` attributes specifically for testing — they're invisible to users, ignored by styling changes, and signal to every developer that removing them will break tests.  </faq-answer-md>  </custom-faq>``
