# Cypress testing | JDLT

## Automated testing using the Cypress testing library

Francisco Dias

over 5 years ago·7 mins read

This is Part 2 of a series of articles I’m writing on automated testing, (specifically React testing), and the value the JDLT team get from it.

In Part 1, [React component testing with Jest and React Testing Library](/content/blog/testing-react-components-with-jest-and-react-testing-library/index.html), I explored how I use automated tests as a way of adding value in my first role as a developer in a company with a large code-base to learn.

Back then, I focussed on Unit testing and Integration testing while using Jest and React Testing Library. This time I’ll be writing about End-to-End testing and how we use it to give us confidence our apps work as intended.

So, what are Unit, Integration and End-to-End tests? Well, most automated tests fall into one of three types:

| Type of Test   | What does it test? It tests …                                                  | Popular tools                                   |
|----------------|--------------------------------------------------------------------------------|------------------------------------------------|
| Unit           | Small independent parts work as expected. Isolated function/classes/algorithm.  | Jest and React Testing Library / Enzyme        |
| Integration     | Several units/parts work together as expected.                                   | Jest and React Testing Library / Enzyme        |
| End-to-End     | Behaves as the user would, clicking/typing around the app.                      | Cypress or Puppeteer and Jest                   |

I'll assume you have a basic knowledge of TypeScript and React. I use a MacBook for development so I'll be using [Zsh](https://ohmyz.sh/) & [Brew](https://brew.sh/) and I also use Yarn rather than NPM. I will be using a React project I worked on previously to show some examples.

Cypress and Jest & Puppeteer seem to get a considerable amount of attention for their accessibility and cost effectiveness when it comes to End-to-End testing. I will therefore produce two articles exploring my experience using these tools with a React app.

This article will focus on Cypress and Part 3 will focus on Jest and Puppeteer.

### What is Cypress?

Cypress is a tool developed by a company of the same name. The test runner is open source and free although they offer paid services that integrate with it.

> Cypress can test anything that runs in a browser

### Why Cypress?

Cypress has been designed to be the End-to-End testing tool of choice and as result it is very reliable and makes testing enjoyable for the developer. It supports testing in different browsers, and it is very easy to get started because there are no servers, drivers, or dependencies to install or configure. Cypress also offers out of the box examples of tests and video recordings of each session which you can leverage.

### Cypress installation

Install Cypress:

```bash
$ npm install cypress --save-dev
```

Or

```bash
$ yarn add -D cypress
```

Adding Cypress commands to scripts in `package.json`:

```javascript
  ...
  "scripts": {
    "cypress-open": "cypress open",
    "cypress-run": "cypress run --browser chrome"
  }
  ...
```

`cypress run –browser chrome` will default to chrome as a browser when running tests.

`Cypress-open` will open the cypress console window while `Cypress-run` just runs tests and when all the tests have run, it shuts down cypress and the browser.

While configuring cypress bear in mind to include `baseUrl` to `tsconfig.json` and make the relevant changes to `cypress.json` should you decide to move the cypress folder from root or define options for when tests are ran (check [Cypress configuration docs](https://docs.cypress.io/guides/references/configuration#Global) for more information).

Example of `cypress.json` should you decide to move the cypress folder to a folder called tests which lives in root folder of the project:

```js
{
  "cypressFolder": "test/cypress",
  "fixturesFolder": "test/cypress/fixtures",
  "integrationFolder": "test/cypress/integration",
  "pluginsFile": "test/cypress/plugins/index.js",
  "screenshotsFolder": "test/cypress/screenshots",
  "videosFolder": "test/cypress/videos",
  "supportFile": "test/cypress/support/index.js"
}
```

To ensure your tests don’t break inadvertently, use of the tag `data-cy` as selector identifiers instead of implementation details such as CSS classes or DOM location. Doing so will improve accuracy in targeting the correct element and it will make your tests more resilient to future changes to the component’s implementation.

### Running tests

What to test in a simple `To Do list` app?

When thinking of your `End to End` tests be clear of the user functions of what you’re testing (e.g. logging in to a user area) and build conditions (e.g. invalid vs valid username and password).

Imagine we have an app to help us manage our `To DO list` and that we want to test it. Our app is very simple therefore our tests will cover:

1. Render without crashing
2. Add a new ‘to do’ item to the list
3. Delete an existing ‘to do’ item

For each scenario there will be a series of different test because the build conditions may change, for example what if the input field is empty or if the name is the same as an existing ‘to do’ item.

**Render without crashing:**

_User functions:_

- n/a

_Build conditions:_

- Render without crashing both with and without connection to server,
- Show alert message when rendering without connection to the server,
- With connection to server render two default `To Do items`,
- Render an input field for typing up new `To Do items`,
- Render an ‘Add’ button for adding `To Do items`.

**Add a new ‘to do’ item to the list:**

_User functions:_

- User can fill the input field,
- User click’s button add to add new ‘to do’ item,

_Build conditions:_

- If the ‘Add’ button is pressed but the input field is empty, prevent a new `To Do item` from being created,
- If the ‘Add’ button is pressed but the input field is empty, show an alert to the user,
- If the ‘Add’ button is pressed and the input field has content, add a new `To Do item`,
- Data being passed down from` To Do list` to `To Do Item` as props, each `To Do Item` should render the text that was passed down to it.

**Delete an existing ‘to do’ item:**

_User functions:_

- When the ‘Delete’ button is pressed for a single` To Do item`, remove that `To Do item` from the App.

_Build conditions:_

- If the first `To Do item` has been removed from the app, the second item should now become the first (and only) item.

### How to test with Cypress?

Cypress will create many examples of tests within the `integration` folder, use it to get more familiar with the syntax and how to structure your tests – delete these when and if not needed. I will therefore add a very simple and straight forward example of a Cypress test. I also recommend you use Cypress docs to get familiar with the extensive number of assertions, commands and utilities.

The naming convention for testing files is to end it on `*_spec.ts`, as per our test below within the file `my-test_spec.ts`. The tests should be placed in the `integration` folder.

```jsx
describe('<App/>', () => {
  before(() => cy.visit('http://localhost:3000/'))

it('Renders without crashing', () => {
    cy.get('span').contains('My Todo List')
  })

describe('The default Ui', () => {
    it('Renders two default todo items', () => {
      cy.get('.row').should('have.length', 2)
    })

it('Has an input field', () => {
      cy.get('input').should('have.length', 1)
    })

it('Has an Add button', () => {
      cy.get('[data-cy="addButton').should('have.text', 'Add')
    })
  })
})
```

All tests use `describe()` blocks and `it()` functions, a similarity you will notice to other tests you may have used in the past. The pattern to follow would be a `describe()` block with `it()` statements inside it.

The `it()` function takes a string which describes what is being tested and a call-back function. The call-back will in general check/find something (cy.get('[data-cy="addButton') the add button in this case and then check an assertion against it (.should("have.text", "Add").

Cypress was built with `flaky` tests (pass or fails periodically without any code changes) in mind but don’t forget to change assertion’s condition to make tests fail as well as pass.

### Summary

End-to-End testing should focus on replicating user behaviour and interact with both the front and back ends as a user would do. Broad tests which cover user function and build conditions are better than implementation details as these are less likely to break.
