Gleb Bahmutov PRO
JavaScript ninja, image processing expert, software quality fanatic
C / C++ / C# / Java / CoffeeScript / JavaScript / Node / Angular / Vue / Cycle.js / functional
EveryScape
virtual tours
MathWorks
MatLab on the web
Kensho
finance dashboards
11 people. Atlanta, Philly, Boston, LA
Fast, easy and reliable testing for anything that runs in a browser
E2E
integration
unit
E2E
integration
unit
putting things together
E2E
integration
unit
Enzyme full rendering
E2E
integration
unit
Will Klein
Your tests
Selenium
Driver
FF, Chrome , IE
Jennifer Shehane @jennifershehane
Your tests
FF, Chrome , IE
slow, flaky, unreliable tests
WebDriver = JSON over HTTP
Your tests
FF, Chrome , IE
Your tests
Headless browser
It is working ... I think
Your tests
Chrome Puppeteer
I need more parts
...
$ npm install -D cypress
it('opens the page', () => {
cy.visit('http://localhost:3000')
cy.get('.new-todo')
.should('be.visible')
})
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)
})
Egghead.io Cypress Course
Free course (same Egghead.io author!)
Cypress documentation
See, share, and fix failing tests.
E2E
integration
unit
E2E
integration
unit
E2E
integration
unit
WAIT A MINUTE!
E2E
integration
unit
it('logs user in', () => {
cy.visit('page.com')
cy.get('#login').click()
})
E2E
integration
unit
import LoginComponent from './src/login'
it('logs user in', () => {
mount(LoginComponent)
cy.get('#login').click()
})
import mount from 'cypress-<X>-unit-test'
E2E
integration
unit
import LoginComponent from './src/login'
it('logs user in', () => {
mount(LoginComponent)
cy.get('#login').click()
})
import mount from 'cypress-<X>-unit-test'
E2E
integration
unit
import LoginComponent from './src/login'
it('logs user in', () => {
mount(LoginComponent)
cy.get('#login').click()
})
import mount from 'cypress-<X>-unit-test'
storybook.js.org
Why ?????
What has changed?
(change in mathematics is usually written using Greek letter "delta")
compare current value with a good value
expect(value).toMatchSnapshot()
Received value does not match stored snapshot 1.
- 3
+ 21
Showing ✅ and 🛑 values works for simple values
it('compares objects', () => {
const o = {
inner: {
a: 10,
b: 20
},
foo: 'foo (changed)',
newProperty: 'here'
}
snapshot(o)
})
changed value
new value
it('compares multi line strings', () => {
snapshot(`
this is a line
and a second line (changed)
with number 42
`)
})
changed line
it('looks the same', () => {
expect(cy.screenshot()).toMatchSnapshot()
})
[FF, FF, FF] [FF, FF, FF] [FF, FF, FF]
[FF, FE, FF] [FF, FF, FF] [FF, FF, FF]
[FF, FE, FF] [FF, FE, FF] [FF, FF, FF]
[FF, FF, FF] [FF, FE, FF] [FF, FF, FF]
...
it('looks the same', () => {
expect(cy.screenshot()).toMatchSnapshot()
})
[FF, FF, FF] [FF, FF, FF] [FF, FF, FF]
[FF, FE, FF] [FF, FF, FF] [FF, FE, FF]
[FF, FE, FF] [FF, FE, FF] [FF, FE, FF]
[FF, FF, FF] [FF, FE, FF] [FF, FF, FF]
...
[FF, FF, FF] [FF, FF, FF] [FF, FF, FF]
[FF, FE, FF] [FF, FF, FF] [FF, FE, FF]
[FF, FE, FF] [FF, FE, FF] [FF, FE, FF]
[FF, FF, FF] [FF, FE, FF] [FF, FF, FF]
...
Shows us WHAT has changed
Shows us WHAT has changed
Welcome!
screenshot difference
DOM
difference
Welcome!
screenshot difference
.logo {
- background-color: #ffffff;
+ background-color: #fefefe;
}
+ <div>Welcome!</div>
style
dom
Welcome!
screenshot difference
.logo {
- background-color: #ffffff;
+ background-color: #fefefe;
}
+ <div>Welcome!</div>
DOM diff tells HOW page has changed
style
dom
.logo {
- background-color: #ffffff;
+ background-color: #fefefe;
}
+ <div>Welcome!</div>
Application behavior difference
style
dom
HTML, styles, code, cookies, storage, API calls, user interaction
test timeline
pixel + DOM difference
API call returned different greeting text
successful test run
failed test run
test timeline
Test history diff explains WHY the page has changed
successful test run
failed test run
HAR on steroids
network + DOM + custom
Error, stack trace, screenshot diff
Find what really happened
describe('User', () => {
it('logs in', () => {
cy.visit(...)
...
})
it('changes primary email', () => {
...
})
it('can find an item', () => {
...
})
})
I don't want to wait 15 minutes
I can't wait 15 minutes
cypress run --record --parallel
version: 2
jobs:
test:
parallelism: 4
CircleCI v2 example
cypress run --record --parallel
a-spec.js
b-spec.js
c-spec.js
d-spec.js
e-spec.js
f-spec.js
...
...
...
...
Done!
By Gleb Bahmutov
Unit testing is hard and time consuming; and worse - the users and the customers do not care! They only want the features working in the production system, everything else is development overhead. If this is the case, how do we improve the web application quality? How do we catch the bugs early? How can we test the deployed system effectively? And finally, how do we get rid of Selenium? This presentation tries to answer many of these questions in a web framework agnostic way. Presented at WeAreDevs Congress 2018, video at https://www.youtube.com/watch?v=p38bIMC-YOU
JavaScript ninja, image processing expert, software quality fanatic