Cypress
Cypress is an end-to-end testing library for testing the entirety of a web app, including its frontend and backend, in a way that closely simulates how real users would use the app. It also has an API for writing integration tests and unit tests.
Cypress tests aim to simulate the user experience as closely as possible. This means that tests you write will usually start up a real browser process, navigate to the URL of your web app, then execute a series of user interactions (eg. clicking on links, buttons, sending keystrokes, etc.) and let you make assertions on how the document should respond and what its contents should have.
The purpose of end-to-end testing this way is to give you confidence that the user can perform critical actions without error. For example, you might have a test that verifies your web app’s authentication system, the purchase of an item, the sending of a message, etc. Unit tests with Knowledge/Engineering/Technologies/Jest, for example, wouldn’t be sufficient for that purpose.
# Writing Tests
In general, the arrange, act, assert pattern for writing unit tests is also a useful way to structure e2e Cypress tests.
# Example
A simple test suite I wrote for my portfolio website.
|
|
Note: describe
and it
are sourced from
Mocha and expect
is sourced from
Chai, two core dependencies of Cypress. Also, Mocha provides context
which is just an alias for describe
.
# API
See Cypress API Documentation. You’ll be frequently chaining many methods together in a single statement.
- Methods like
cy.get
return a DOM element that can be further chained with methods liketype
,click
,contains
, etc. - Some methods like
cy.clearCookies
do not yield anything that you can chain further methods on.
Top-Level Methods
|
|
Note: all of these cy.*
statements execute asynchronously.
Chained Methods:
|
|
The chainer
argument is a stringified chainer from
Chai,
Chai-jQuery,
Sinon-Chai, which are dependencies of Cypress.
# Aliases
You must always chain commands off of an invocation on cy.*
otherwise commands won’t be properly enqueued. Eg. if you’re doing const elem = cy.get(...); elem.then(...)
, then you need to use an alias instead, as shown below.
|
|
# Fixtures
In Cypress, fixtures are a collection of static test data that can be used by tests. They’re located at cypress/fixtures
and are typically .json files, but can also be .js, image files, etc. The common usage of fixtures is in stubbing network requests.
|
|
# Reusuable Custom Commands
Cypress gives you many useful commands, however you might need some custom reusable helper functions to help with stubbing network requests, for example. You define custom helpers in cypress/support/commands.ts
by doing the following:
|
|
This makes your helpers available under the cy
object, eg. from the above example, we’d be able to access cy.helperName()
from any test.
# Mocking Network Requests
Often, you’ll want to test the frontend independently of the backend, that is, you might not actually want your frontend to make requests to your backend server. You can do this by stubbing API requests with responses using
cy.intercept
.
|
|
# Tradeoffs
When you stub network requests, you’re no longer writer ’true’ end-to-end tests. Your tests are more isolated and generally less flaky since it has fewer points of failure, however you are straying away from testing the real user experience.
If you write true end-to-end tests, then:
- If you have a database, you’d have to seed it to generate state.
- Tests are possibly much slower since they’ll actually go through the full backend request-handling logic.
- It’ll be hard to test for edge cases like network failure.
It’s recommended to maintain a balance of both stubbed tests and true end-to-end tests (especially for the critical user actions in your application like authentication).
# Seeding the Database
# Cypress CLI
The Cypress package ships with a powerful CLI. Official reference.
Some basic commands to know and consider adding to the NPM scripts inside package.json
:
|
|
# Cypress CI
I used the GitHub Actions workflow YAML file provided by the official docs to run Cypress in a CI pipeline.