Cypress.io
When testing is easy, developers build better things faster and with confidence.
use up/down and left/right keys to navigate these slides
https://lizkeogh.com/2019/07/02/off-the-charts/
https://gleb.dev/blog/climate-emergency
+3 degrees Celsius will be the end.
If there is a company that fights global climate catastrophe and needs JavaScript and testing skills - I will do for free.
Example: https://fab.earth
30 people. Atlanta, Philly, Boston, NYC, the World
Fast, easy and reliable testing for anything that runs in a browser
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)
})
typical Cypress end-to-end test
"Interactive Mode"
"Headless Mode"
repo made public
Oct 2017
❤️ Hundreds of tweets like this one ❤️
these slides
E2E
integration
unit
const add = (a, b) => a + b
it('adds numbers', () => {
expect(add(2, 3)).to.equal(5)
})
import { HelloState } from '../src/hello-x.jsx'
import { HelloState } from '../src/hello-x.jsx'
import React from 'react'
it('changes state', () => {
cy.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!')
})
it('yields result that has log messages', () => {
cy.api({ url: '/' }, 'hello world')
.then(({ messages }) => {
const logs = Cypress._.filter(messages, {
type: 'console',
namespace: 'log'
})
expect(logs, '1 console.log message').to.have.length(1)
expect(logs[0]).to.deep.include({
type: 'console',
namespace: 'log',
message: 'processing GET /'
})
})
})
it('adds todos', () => {
cy.visit('/')
cy.get('.new-todo')
.type('write code{enter}')
.type('write tests{enter}')
.type('deploy{enter}')
cy.get('.todo').should('have.length', 3)
})
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
// main.js
const { app } = require('electron')
const MainBrowserWindow = require('./main_browser_window')
let win
function createWindow () {
win = MainBrowserWindow()
win.loadURL('http://localhost:4600')
}
app.on('ready', createWindow)
// cypress/integration/spec.js
it('clicks', () => {
// window creation and url load
cy.electronVisitUrl('./main_browser_window.js',
'http://localhost:4600')
cy.get('button')
.click()
.click()
cy.get('#clicked').should('have.text', '2')
})
it('has good contrast', () => {
cy.visit('/')
cy.injectAxe()
cy.checkA11y({
runOnly: ['cat.color'],
})
})
example
Are we testing all features?
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
typical Todo application http://todomvc.com/
Are we testing all features?
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
it('adds todos', () => { ... })
it('completes todos', () => {
... })
Ohhh, we don't have a test for Feature C
Are we testing all features?
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
it('adds todos', () => { ... })
it('completes todos', () => {
... })
it('deletes todos', () => {
... })
Are we testing all features?
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
source code
Are we testing all features?
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
source code
it('adds todos', () => { ... })
it('completes todos', () => {
... })
Are we testing all features?
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
source code
it('adds todos', () => { ... })
it('completes todos', () => {
... })
green: lines executed during the tests
red: lines NOT executed during the tests
Are we testing all features?
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
source code
it('adds todos', () => { ... })
it('completes todos', () => {
... })
it('deletes todos', () => {
... })
Are we testing all features?
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
source code
it('adds todos', () => { ... })
it('completes todos', () => {
... })
it('deletes todos', () => {
... })
Code coverage from tests indirectly
measures implemented features tested
Feature A
User can add todo items
Feature B
User can complete todo items
Feature C
User can delete todo items
❚❚❚❚❚
❚❚❚❚❚ ❚❚ ❚❚❚❚
❚❚❚
❚❚❚❚
❚❚❚ ❚❚❚❚❚❚❚
❚❚❚❚
❚❚
❚❚❚❚❚ ❚❚
❚❚❚❚❚❚
❚❚❚
❚❚❚❚❚
❚❚❚❚❚
❚❚❚❚
❚❚❚❚
❚❚❚❚❚❚ ❚❚❚ ❚❚
❚
❚❚
source code
it('adds todos', () => { ... })
it('completes todos', () => {
... })
it('deletes todos', () => {
... })
Unrealistic tests; subset of inputs
code does not implement the feature correctly
it('adds todos', () => {
cy.visit('/')
cy.get('.new-todo')
.type('write code{enter}')
.type('write tests{enter}')
.type('deploy{enter}')
cy.get('.todo').should('have.length', 3)
})
it('adds todos', () => {
cy.visit('/')
cy.get('.new-todo')
.type('write code{enter}')
.type('write tests{enter}')
.type('deploy{enter}')
cy.get('.todo').should('have.length', 3)
})
it('adds todos', () => {
cy.visit('/')
cy.get('.new-todo')
.type('write code{enter}')
.type('write tests{enter}')
.type('deploy{enter}')
cy.get('.todo').should('have.length', 3)
})
We have tested "add todo"
Need tests
Write more end-to-end tests
Ughh, missed it
What is this code doing?
Can I write an E2E test to hit this code?
This code should be unreachable from the user interface
import {getVisibleTodos} from '../../src/selectors'
describe('getVisibleTodos', () => {
it('throws an error for unknown visibility filter', () => {
expect(() => {
getVisibleTodos({
todos: [],
visibilityFilter: 'unknown-filter'
})
}).to.throw()
})
})
@cypress/code-coverage plugin
We got this line from the unit test
@cypress/code-coverage plugin
Nice job, @cypress/code-coverage plugin
combines e2e and unit test coverage automatically
E2E
integration
unit
Really important to users
Really important to developers
Tutorials
Api
Examples
$ npx cypress run --record
Upload test artifacts from any CI
test output, video, screenshots
WIP
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
https://www.cypress.io/blog/2019/07/29/github-integration-for-the-cypress-dashboard/
This dark red color looks familiar ...
Nice contrast on the footer labels
todomvc-app-css@2.3.0
todomvc-app-css@2.2.0
Oh yeah, it was my PR!
it('has good contrast when empty', () => {
cy.visit('/')
cy.injectAxe()
cy.checkA11y({
runOnly: ['cat.color'],
})
})
it('has good contrast with several todos', () => {
cy.visit('/')
cy.injectAxe()
cy.get('.new-todo')
.type('learn testing{enter}')
.type('be cool{enter}')
cy.get('.todo-list li').should('have.length', 2)
cy.checkA11y({
runOnly: ['cat.color'],
})
})
uses
Cypress tests
use
Cypress tests
uses
Cypress tests
Thank you ❤️
cypress run --tag develop
cypress run --tag staging
cypress run --tag production
cypress run --tag nightly
cypress run --tag hourly
cypress run --group desktop-uk --tag develop,nightly
cypress run --group desktop-de --tag production
failing 15 of 3000 tests
Source of command errors unclear
No full diffs
App Errors
Assertions
Code Frames
Open your text editor right at the error location
Click on the failed test attempt to see the command log
By Cypress.io
The demand for developer productivity has never been greater. We are expected to continuously improve and release code, often several times a day. But how do we achieve this kind of velocity without introducing bugs? How can our codebase adapt to changing business requirements, while still keeping technical debt to a minimum? Video at https://www.youtube.com/watch?v=2gP1-TNDzK4
When testing is easy, developers build better things faster and with confidence.