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

🌐 www.cypress.io

Painless Testing for React Applications

By Gleb Bahmutov

Painless Testing for React Applications

Testing is hard. End to end testing is really hard. But maybe it is hard because our tools are not up to task. What if we could redesign the testing experience from the ground up to be fast, useful and effective? Let me show you how to quickly test a React application using Cypress.io - an open source modern test runner that replaces Selenium. Don't believe the hype - come see Cypress demo for yourself, and I guarantee you will start writing tests. Presented at ReactJS Boston meetup April 2018. Video at https://www.youtube.com/watch?v=lgurVvQsOTY

  • 871
Loading comments...

More from Gleb Bahmutov