Painless testing for React applications

ReactJS Boston

C / C++ / C# / Java / CoffeeScript / JavaScript / Node / Angular / Vue / Cycle.js / functional

EveryScape

virtual tours

MathWorks

MatLab on the web

Kensho

finance dashboards

8 people. Atlanta, Philly, Boston, LA

Fast, easy and reliable testing for anything that runs in a browser

E2E

integration

unit

Smallest pieces

Testing Pyramid △

Unit tests pass...

E2E

integration

unit

Component

(enzyme with full rendering)

E2E

integration

unit

Website

You go to a dealership to take a new car for a test drive, not to measure its wheels

planning

coding

deploying

staging / QA

production

E2E

E2E

Users

planning

coding

deploying

staging / QA

production

💵

E2E

E2E

Users

💵

🐞$0

💵

💵

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💵

💵

planning

coding

deploying

staging / QA

production

💵

E2E

E2E

Users

💵

🐞$0

💵

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

💰

Answer: E2E

💵

💵

💵

Let's test!

  1. Open and control real browser

Your tests

Selenium

Driver

FF, Chrome , IE

Let's test!

  1. Open and control real browser

Your tests

FF, Chrome , IE

slow, flaky, unreliable tests

Let's test!

Your tests

FF, Chrome , IE

Let's test!

Your tests

Headless browser

What is happening?

$ npm install -D cypress
// ui-spec.js
it('loads the app', () => {
  cy.visit('https://some-url.com')
  cy.get('.todoapp').should('be.visible')
})
const visit = () => cy.visit('/')

describe('UI', () => {
  beforeEach(resetDatabase)
  beforeEach(visit)

  context('basic features', () => {
    it('starts with zero items', () => {
      cy.get('.todo-list')
        .find('li')
        .should('have.length', 0)
    })
  })
})

Cypress demo

  • Opening a page
  • Typical test
  • Failing test
  • API and documentation
  • Recording tests
  • CI setup

Sliding down the testing pyramid

E2E

integration

unit

Need:

  • real browser (DOM, storage, ...)
  • state cleanup between tests
  • stubbing server

E2E

integration

unit

Need:

  • real browser (DOM, storage, ...)
  • state cleanup between tests
  • stubbing server

WAIT A MINUTE!

Interact, inspect, use

cypress-vue-unit-test

cypress-react-unit-test

cypress-svelte-unit-test

cypress-hyperapp-unit-test

...

$ npm install -D cypress cypress-react-unit-test
import { HelloSpider } from './src/hello-spiderman.jsx'
import React from 'react'
import { mount } from 'cypress-react-unit-test'
  

React component test demo with Cypress

it('works', () => {
  mount(<HelloSpider />)
  // start testing!
  cy.contains('Hello Spider-man!')
})
  

E2E

integration

unit

Why are they called

cypress-<X>-unit-test ?!

Cypress for Integration

E2E

integration

unit

function add(a, b) {
  return a + b
}
add(a, b)

outputs

inputs

a, b

returned value

it('adds', () => {
  expect(add(2,3)).to.equal(5)
})

Unit test

function add(a, b) {
  const el = document.getElementById('result')
  el.innerText = a + b
}
add(a, b)

outputs

inputs

a, b

DOM

it('adds', () => {
  add(2,3)
  const el = document.getElementById('result')
  expect(el.innerText).to.equal('5')
})

Integration test?

function add() {
  const {a, b} = 
    JSON.parse(localStorage.getItem('inputs'))
  const el = document.getElementById('result')
  el.innerText = a + b
}
add(a, b)

outputs

inputs

localStorage

DOM

it('adds', () => {
  localStorage.setItem('inputs') =
    JSON.stringify({a: 2, b: 3})
  add()
  const el = document.getElementById('result')
  expect(el.innerText).to.equal('5')
})

Integration test?

component

outputs

inputs

DOM,

localStorage,

location,

HTTP,

cookies

WW, SW,

...

DOM,

localStorage,

location,

HTTP,

cookies

WW, SW,

...

component

outputs

inputs

DOM,

localStorage,

location,

HTTP,

cookies

WW, SW,

...

DOM,

localStorage,

location,

HTTP,

cookies

WW, SW,

...

Unit test

Set up

Assert

Mount

E2E

unit

it('logs user in', () => {
  cy.visit('page.com')
  cy.get('#login').click()
})

E2E

unit

it('logs user in', () => {
  mount(LoginComponent)
  cy.get('#login').click()
})

E2E

unit

Use same syntax, life cycle and Cypress API

🔋🔋🔋🔋🔋🔋🔋🔋🔋🔋🔋

Thank you

@cypress_io

@bahmutov