Cypress 🤖
We prefer using Cypress for end to end testing for our frontend applications. It is easy to set up and it provides some modern features which are convenient while writing end to end tests.
Configuration
-
We use Typescript with our frontend applications. After installing Cypress and Typescript we need to configure
Cypress
to useTypescript
. A sampletsconfig.json
file looks like below{ "compilerOptions": { "target": "es5", "lib": ["es5", "dom"], "types": ["cypress"], "moduleResolution": "node", "resolveJsonModule": true }, "include": [ "**/*.ts" ], "files": [ "./support/index.d.ts" ] }
-
Some scripts should be added to the
package.json
file for easier usage ofCypress
"scripts": { "cypress:open": "cypress open", "cypress:run": "cypress run" }
-
Base URL must be specified in the
cypress.json
to avoid repetitive definition of base URL in each test.{ "baseUrl": "http://localhost:3000", }
Formatting
-
-
-
-
Do not leave line breaks after
context
ordescribe
blocks.describe('User authentication', () => { context('given valid user credentials', () => { it('redirects to the landing page', () => {}) }) })
describe('User authentication', () => { context('given valid user credentials', () => { it('redirects to the landing page', () => {}) }) })
-
Leave one line return around
context
andit
blocks.describe('User authentication', () => { context('given valid user credentials', () => { it('redirects to the landing page', () => { ... }) it('shows user menu', () => { ... }) }) context('given INVALID user credentials', () => { it('shows login error', () => { ... }) }) })
describe('User authentication', () => { context('given valid user credentials', () => { it('redirects to the landing page', () => { ... }) it('shows user menu', () => { ... }) }) context('given INVALID user credentials', () => { it('shows login error', () => { ... }) }) })
Naming
-
camelCase
for file names. -
Each test file name must have the suffix
.spec
to distinguish test files from source files.integration/ ├── UserAuthentication/ │ ├── login.spec.ts │ ├── signup.spec.ts ├── UserSearch/ │ ├── searchByEmail.spec.ts support/ ├── commands.ts/
To enforce this rule a configuration should be added in the
cypress.json
file{ "testFiles": ["**/*.spec.ts"] }
-
Use
describe
to group tests using the file name for the description.// searchByEmail.spec.json describe('Search by email', () => {})
-
Use
context
to describe testing preconditions.context
block descriptions must always start withwhen
orgiven
, and be in the form of a sentence with proper grammar.it('redirects to landing page if user credentials are valid', () => {})
context('given valid user credentials', () => { it('redirects to the landing page', () => {}) })
-
Do NOT prefix
it
block descriptions withshould
. Use the imperative tone instead.it('should redirect to landing page', () => {})
it('redirects to the landing page', () => {})
Element selector
Avoid using highly brittle HTML selectors that are subject to change with target elements. Use data-test-id
attributes
to provide context to your selectors and insulate them from CSS or JS changes.
<div data-test-id="userProfile"></div>
Functions
Use ES6
arrow function for describe/context/it
block
describe('User authentication', function() {
context('given valid user credentials', function() {
it('redirects to the landing page', function() {})
})
})
describe('User authentication', () => {
context('given valid user credentials', () => {
it('redirects to the landing page', () => {})
})
})
Stub HTTP requests/responses
From Cypress v6.0.0
a new command cy.intercept()
has been added to stub HTTP requests and responses. This command can be used to specify HTTP fixtures as well.
cy.intercept('http://example.com/widgets', { fixture: 'widgets.json' })
All information about this command can be found in the documentation.
Custom commands
Cypress
supports user-defined commands. Custom commands are beneficial for automating a workflow repeated in tests. It
can significantly DRY-up tests.
If we have an application that requires login on each page then this can be made into a custom command
Cypress.Commands.add('login', () => {
cy.visit('/login')
cy.get('input[name=email]').type('[email protected]')
cy.get('input[name=password]').type(`'password'{enter}`)
})
Then this command can be easily used in tests as cy.login()
. When using typescript adding a custom command
requires a TypeScript
definitions file. More can be found in the documentation.