Distinguished Engineer
@bahmutov
C / C++ / C# / Java / CoffeeScript / JavaScript / Node / Angular / Vue / Cycle.js / functional
50 people. Atlanta, Philly, Boston, NYC, the world
Fast, easy and reliable testing for anything that runs in a browser
Quality software behaves the way users expect it to behave
E2E
integration
unit
E2E
integration
unit
E2E
integration
unit
E2E
integration
unit
E2E
integration
unit
Really important to users
Really important to developers
E2E
integration
unit
$ npm install -D cypress
// ui-spec.js
it('loads the app', () => {
cy.visit('http://localhost:3000')
cy.get('.todoapp').should('be.visible')
})
Mocha BDD syntax
Chai assertions
it('adds 2 todos', () => {
cy.visit('http://localhost:3000')
cy.get('.new-todo')
.type('learn testing{enter}')
.type('be cool{enter}')
cy.get('.todo-list li')
.should('have.length', 2)
})
$ npx cypress open
DOM
storage
location
cookies
Cypress tests run in the same browser
DOM
storage
location
cookies
Cypress acts as a proxy for your app
Cypress tests run in the browser and Cypress can interact with the OS via its plugin file that runs in the Node process
describe('intercept', () => {
it('returns different fruits every 30 seconds', () => {
cy.clock()
// return difference responses on each call
// notice the order of the intercepts
cy.intercept('/favorite-fruits', ['kiwi 🥝']) // 3rd, 4th, etc
cy.intercept('/favorite-fruits', { times: 1 }, ['grapes 🍇']) // 2nd
cy.intercept('/favorite-fruits', { times: 1 }, ['apples 🍎']) // 1st
cy.visit('/fruits.html')
cy.contains('apples 🍎')
cy.tick(30000)
cy.contains('grapes 🍇')
// after using the first two intercepts
// forever reply with "kiwi" stub
cy.tick(30000)
cy.contains('kiwi 🥝')
cy.tick(30000)
cy.contains('kiwi 🥝')
cy.tick(30000)
cy.contains('kiwi 🥝')
})
})
controls app clock and mocks network
describe('intercept', () => {
it('returns different fruits every 30 seconds', () => {
cy.clock()
// return difference responses on each call
// notice the order of the intercepts
cy.intercept('/favorite-fruits', ['kiwi 🥝']) // 3rd, 4th, etc
cy.intercept('/favorite-fruits', { times: 1 }, ['grapes 🍇']) // 2nd
cy.intercept('/favorite-fruits', { times: 1 }, ['apples 🍎']) // 1st
cy.visit('/fruits.html')
cy.contains('apples 🍎')
cy.tick(30000)
cy.contains('grapes 🍇')
// after using the first two intercepts
// forever reply with "kiwi" stub
cy.tick(30000)
cy.contains('kiwi 🥝')
cy.tick(30000)
cy.contains('kiwi 🥝')
cy.tick(30000)
cy.contains('kiwi 🥝')
})
})
controls app clock and mocks network
Tutorials
Api
Examples
Cypress Tips & Tricks
full video of the run, screenshots of every failure
Making it easy for users is not easy
[[plugins]]
# local Cypress plugin will test our site after it is built
package = "netlify-plugin-cypress"
version: 2.1
orbs:
cypress: cypress-io/cypress@1
workflows:
build:
jobs:
- cypress/run
name: End-to-end tests
on: [push]
jobs:
cypress-run:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: cypress-io/github-action@v2
test output, video, screenshots
I have 100s of tests ...
$ npx cypress run --record --parallel
Cypress v3.1.0
Spin N CI machines and
Text
# machines | run duration | time savings |
---|---|---|
1 | 22:50 | ~ |
2 | 11:47 | 48% |
3 | 7:51 | 65% |
4 | 5:56 | 74% |
6 | 3:50 | 83% |
8 | 3:00 | 87% |
10 | 2:19 | 90% |
10 machines = 10x speed up
{
"retries": {
// Configure retry attempts for `cypress run`
// Default is 0
"runMode": 2,
// Configure retry attempts for `cypress open`
// Default is 0
"openMode": 0
}
}
import { HelloState } from '../src/hello-x.jsx'
import { mount } from '@cypress/react' // or @cypress/vue
import React from 'react'
it('changes state', () => {
mount(<HelloState />)
cy.contains('Hello Spider-man!')
const stateToSet = { name: 'React' }
cy.get(HelloState).invoke('setState', stateToSet)
cy.get(HelloState)
.its('state')
.should('deep.equal', stateToSet)
cy.contains('Hello React!')
})
$ npm i -D @cypress/instrument-cra
+ @cypress/instrument-cra@1.0.0
"start": "react-scripts -r @cypress/instrument-cra start"
it('draws pizza correctly', function () {
cy.percySnapshot('Empty Pizza')
cy.enterDeliveryInformation()
const toppings = ['Pepperoni', 'Chili', 'Onion']
cy.pickToppings(...toppings)
// make sure the web app has updated
cy.contains('.pizza-summary__total-price', 'Total: $12.06')
cy.percySnapshot(toppings.join(' - '))
})
<style type="text/css">
.st0{fill:#FFD8A1;}
.st1{fill:#E8C08A;}
- .st2{fill:#FFDC71;}
+ .st2{fill:#71FF71;}
.st3{fill:#DFBA86;}
</style>
changes crust color SVG
1. Make pull request with visual changes
catch visual changes in the pull request
2. Review & approve visual changes
2. Review & approve visual changes
// app code
var model = new app.TodoModel('react-todos')
if (window.Cypress) {
window.model = model
}
// test code
export const addTodos = (...todos) => {
cy.window()
.its('model')
.should('be.an', 'object')
.invoke('addTodo', ...todos)
}
E2E
+ visual testing
component
unit
api
My view of testing efficiency
@cypress_io
@bahmutov