Cypress.io
When testing is easy, developers build better things faster and with confidence.
https://lizkeogh.com/2019/07/02/off-the-charts/
+3 degrees Celsius will be the end.
flying fast and high
steaming trail
fast propeller
blue like a policeman
2 seats
Number 576
Very tall
Lots of flags π©π©π©π©π©
Strong cannons π£
Climbing ropes
Attacks other ships
Tries to find treasure ππ°Β
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
these slides
π¦ @bahmutov
Prettier
ESLint
@ts-check
Code is easier to read and understand
Actual linter: catches JS things that can go wrong
Strict(er) linter: catches JS and your type errors
Step 1: code in JS
const add = (a, b) => a + b
add(2, 'foo')
Step 2: add comment
VSCode extension "Document This"
https://marketplace.visualstudio.com/items?itemName=joelday.docthis
Nice: IntelliSense
Step 3: @ts-check
CLI tsc check
$ npx tsc --noEmit --allowJs app.js
app.js:9:8 - error TS2345: Argument of type '"foo"' is not assignable
to parameter of type 'number'.
9 add(2, 'foo')
~~~~~
Found 1 error.
IntelliSense in JS test files when you are using Cypress. Hovering over ".type" command
Documentation page for "cy.type" at https://on.cypress.io/type
I think using it
Static types, linting, JSDoc comments, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples, examples
E2E
integration
unit
(click down arrow to see types of tests)
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
it('has good contrast', () => {
cy.visit('/')
cy.injectAxe()
cy.checkA11y({
runOnly: ['cat.color'],
})
})
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
Tests are kept short because they run in the terminal. Their shortness helps to debug a failed test.
Go ahead
class MasterForm extends React.Component {
constructor (props) {
super(props)
if (window.Cypress) {
window.app = this
}
}
...
}
Now tests can control the application directly
expose app reference
cy.contains('Next').click()
cy.log('Second page')
cy.contains('h1', 'Book Hotel 2')
cy.window()
.its('app.state')
.should('deep.equal', startOfSecondPageState)
cy.window()
.its('app')
.invoke('setState', startOfSecondPageState)
cy.log('Second page')
cy.contains('h1', 'Book Hotel 2')
cy.get('#username').type('JoeSmith', typeOptions)
π¦ @bahmutov
Angry or happy? Let me know @bahmutovΒ Β Β @cypress_io
By Cypress.io
This presentation looks at the variety of ways we can test a typical web application. From unit tests to end-to-end, from functional to visual testing, from happy paths to the edge cases - there are a lot of solutions available, and each new technique gives us additional confidence in our application. This presentation will teach you how to guide test writing using code coverage, how to reach into the application code to test edge cases, and how to decide when to stub a network API request and when to let it go through.
When testing is easy, developers build better things faster and with confidence.