Effective Frontend Testing

Slides, links, etc.

maxpou.fr/speaking/

πŸ‘‹ Hey I'm Max


  • πŸ‘¨πŸΌβ€πŸ’» Sr. Frontend Engineer @VSware (#Vue.js)
  • πŸŽ’ Nomad / Serial Traveler
  • 🍺 Beer appreciator
  • πŸ“¬ @_maxpou

Effective Frontend Testing

Why not to test?

  • ⏳ "We don't have time to test!"
  • 🐌 "Test slow me down!" (creation + maintenance)
  • πŸ€·β€β™‚οΈ "We don't know how to test and what to test!"

πŸ’ͺ Why we need test

  • πŸ€— Confidence
  • 🍻 Deploy to prod on Friday? Why not!
  • πŸ“– Document codebase
  • πŸ‘Š Helps to write better code
  • πŸ’Έ Reduce maintenance cost

Testing pyramid

Testing pyramid

Testing in reality

testing dorito

https://twitter.com/denvercoder/status/960752578198843392

Testing pyramid

Testing pyramid

...but...

titanic

Testing Trophy

Testing Trophy

Β© Kent C. Dodds

Static Testing

ESLint

Yes I know it can be annoying... but it catches:

  • πŸ‘ dumb errors
    (== instead of ===, unused variables...)
  • πŸ‘ avoid steril debates / bikeshedding
    (tab vs space, semicolons...)

➑ Helps to focus on what really matter ❀️


πŸ‘¨πŸ»β€πŸ’Ό easily extendable - i.e. companies internal rules

Unit tests

Jest

watch mode, run with patterns, snapshot testing, well integrated in VSCode (debug mode ❀️)

Example

unit-example

Method AAA - Arrange-Act-Assert

How to test component?

Treat your component as a black box

don't test internals!

Definition of a good test

  • 🐟 Dead simple
  • ⚑️ Lightning fast (nobody like to wait!)
  • 🀯 Doesn't test external library
  • πŸ›Œ Cover the most common usages

Code Coverage

Code Coverage Report

How code coverage works is generated?

how-cc-works part 1 how-cc-works part 2

How much?

cc and sweet pot

πŸ’© Code coverage BS

Code coverage BS

High coverage !== bug proof

⚠️Covered code !== tested code

cc-side-effect

Write test for confidence

Leave coverage to your manager

[πŸ“ maxpou.fr] Why you shouldn't pay too much attention to your code coverage

πŸ“Έ Snapshot testing

Example

      
const vatCalculator = require('./vatCalculator')

it('return the expected VAT', () => {
  const menu = [
    { item: '🍺', price: 3 },
    { item: 'πŸ”', price: 5.5 }
  ]
  console.log(vatCalculator(menu))
});
      
      
// 3.55
      
    

Example

      
const vatCalculator = require('./vatCalculator')

it('return the expected VAT', () => {
  const menu = [
    { item: '🍺', price: 3 },
    { item: 'πŸ”', price: 5.5 }
  ]
  expect(vatCalculator(menu)).toBe(3.55)
});
      
    

Example

      
const vatCalculator = require('./vatCalculator')

it('return the expected VAT', () => {
  const menu = [
    { item: '🍺', price: 3 },
    { item: 'πŸ”', price: 5.5 }
  ]
  expect(vatCalculator(menu)).toMatchSnapshot()
});
      
    

// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`return the expected VAT 1`] = `3.55`;
    
snapshot

πŸ‘ Good usecase


it('should render correctly', () => {
  const wrapper = mount(Alert, {
    propsData: {
      message: 'Test message',
    },
  });
  expect(wrapper.html()).toMatchSnapshot();
});      

exports[`Alert.vue should render correctly 1`] = `
"
Test message
" `;

Misconceptions

  • πŸ‘€ Snapshot testing is NOT visual regression testing
  • πŸ’πŸ»β€β™€οΈ Not only for DOM testing!!!
  • πŸ™…β€β™‚οΈ Purpose is not to replace existing assertions.
    ➑ lazy way to provide test where there's not!
  • πŸ‘ Don’t fall into the temptation of quickly update snapshot without checking the real change!

πŸ€” Huge Snapshot?

  • 🎍 Tests are about confidence
  • πŸ™…β€β™€οΈ Nobody like to review huge snapshots
  • πŸ‘ Use something else! (or shallowMount)

πŸ‘Œ eslint: jest/no-large-snapshots (default: 12 lines)

Integration test

unit vs. integration test

Vue Testing Library

Testing Made it Easy

      
import { render, screen } from '@testing-library/vue'
import App from '../src/pages/App.vue'

it('should render a product', () => {
  render(App)

  screen.getByText('Calzone')
  screen.getByText('The best pizza ever!')
  screen.getByText('$8.00')
})
      
    

Vue Testing Library

Testing Made it Easy

      
import { render, screen } from '@testing-library/vue'
import App from '../src/pages/App.vue'

it('should add items to basket', async () => {
  await render(App)

  await fireEvent.click(screen.getByLabelText('Add Calzone'))
  await fireEvent.click(screen.getByLabelText('Add Margherita'))
  await fireEvent.click(screen.getByLabelText('Add Margherita'))

  expect(screen.getByRole('navigation')).toHaveTextContent('Basket (3)')
  await fireEvent.click(screen.getByText('Basket (3)'))

  await screen.findByText('Total')
  screen.getByText('$19.00')
})
      
    

Mocks

  • πŸ‘Ž Mocks sucks
  • Every time we mock, we diverge from the real world scenario.
  • 🀝 Ok for:
    • External calls (HTTP GET/POST/...)
    • Browsers API (local/session storage, navigator...)

Mount vs. ShallowMount

Mount vs. ShallowMount

Working together

goat on horse

The importance of automation

πŸ‘Œ Following cc evolution

Following cc evolution

github.com/maxpou/gitvub/pull/36

Need more tests?

Totem Team

totem

If your test is red,

don't delete it, fix it!

      
// it('should...', () => {
//   expect(actions.resetStore).toHaveBeenCalled();
//   expect(actions.getXXX).toHaveBeenCalled();
//   expect(actions.getYYY).toHaveBeenCalled();
// });
      
    

πŸ™

Testing is not a "nice to have"

It's a delivrable

Thank you!

Backup

Jason Lengstorf – Building a testing- and quality-driven culture at IBM