Cypress Best Practices
Tags: testing, javascript, e2e, bestpractices
Published: 2021-11-10 18:57:05

{% youtube PPZSySI5ooc %}

## Organizing Tests, Logging In, Controlling State
- **Anti-Pattern:** Sharing page objects, using your UI to log in, and not taking shortcuts.
- **Best Practice:** Test specs in isolation, programmatically log into your application, and take control of your application's state.

## Selecting Elements
- **Anti-Pattern:** Using highly brittle selectors that are subject to change.
- **Best Practice:** Use `data-*` attributes to provide context to your selectors and isolate them from CSS or JS changes

| Selector                  | Recommended | Notes           |
|---------------------------|-------------|-----------------|
| `cy.get('button').click()`| **Never**   | Worst - too generic, no context|
| `cy.get('.btn.btn-large').click()`| **Never**| Bad. Coupled to styling. Highly subject to change|
| `cy.get('#main').click()`| **Sparingly** | Better. But still coupled to styling or JS event listeners.|
| `cy.get('[name=submission]').click()` | **Sparingly** | Coupled to the name attribute which has HTML semantics. |
| `cy.contains('Submit').click()` | **Depends** | Much better. But still coupled to text content that may change. |
| `cy.get('[data-cy=submit]').click()` | **Always** | Best. Isolated from all changes.|

## Assigning Return Values
- **Anti-Pattern:** Trying to assign the return value of Commands with `const`, `let`, or `var`.
- **Best Practice:** Use [closures to access and store](https://docs.cypress.io/guides/core-concepts/variables-and-aliases)

## Visiting External Sites
- **Anti-Pattern:** trying to visit or interact with sites or servers you do not control
- **Best Practice:** Only test what you control. Try to avoid requiring a 3rd party server. When necessary, always use `cy.request()` to talk to 3rd party servers via their APIs.

## Having tests rely on the state of previous tests
- **Anti-Pattern:** Coupling multiple tests together
- **Best Practice:** Tests should always be able to be run independently from one another **and still pass.**

## Creating "tiny" tests with a single assertion
- **Anti-Pattern:** Acting like you're writing unit tests.
- **Best Practice:** Add multiple assertions and don't worry about it

## Using `after` or `afterEach` hooks
- **Anti-Pattern:** Using `after` or `afterEach` hooks to clean up state.
- **Best Practice:** Clean up state **before** tests run.

## Unnecessary Waiting
- **Anti-Pattern:** Waiting for arbitrary time periods using `cy.wait(Number)`
- **Best Practice:** Use route aliases or assertions to guard Cypress from proceeding until an explicit condition is met

## Web Servers
- **Anti-Pattern:** Trying to start a web server from within Cypress scripts with `cy.exec()` or `cy.task()`.
- **Best Practice:** Start a web server prior to running Cypress

## Setting a global baseUrl
- **Anti-Pattern:** Using `cy.visit()` without setting a `baseUrl`.
- **Best Practice:** Set a `baseUrl` in your [configuration file (cypress.json by default)](https://docs.cypress.io/guides/references/configuration)

You can read our entire Best Practices Guide at [https://docs.cypress.io/guides/references/best-practices](https://docs.cypress.io/guides/references/best-practices)