Cypress testing | JDLT
Automated testing using the Cypress testing library
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, 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. E.g. Shared functions usually found in the utils folder. | Jest and React Testing Library / Enzyme |
Integration | Several units/parts work together as expected. E.g. Two React components where one is the parent while passing props to its child. | Jest and React Testing Library / Enzyme |
End-to-End | Behaves as the user would, clicking/typing around the app. Runs an entire app in the browser and interacts with both front and back end as a user would. Like a robot user! | 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 & Brew 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:
$ npm install cypress --save-dev
Or
$ yarn add -D cypress
Adding Cypress commands to scripts in package.json
:
...
"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 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:
{
"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:
- Render without crashing
- Add a new ‘to do’ item to the list
- 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
toTo Do Item
as props, eachTo 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 thatTo 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.
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')
})
})
})
;<button
data-cy="addButton"
type="submit"
className="btn btn-primary col-4 col-sm-2 mb-2"
>
Add
</button>
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.
Further reading
Ready to dive in?
At JDLT, we manage IT systems & build custom software for organisations of all sizes.
Get in touch to see how we can help your organisation.
Book a call