Effective Frontend Testing

Slides, links, etc.

maxpou.fr/speaking/

πŸ‘‹ Hey I'm Max


  • πŸ‘¨πŸΌβ€πŸ’» Sr. Frontend Engineer @VSware (Vue.js)
  • πŸŽ’ Digital Nomad & ✈️ Serial Traveler
  • 🍺 Beer appreciator

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

Flow

Unit tests

Tips: use Jest

Because using 3 packages for testing is confusing ...and also: 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:

  • props
  • events (onClick, onSelect...)
  • lifecycle events (mounted, destroyed...)
  • event emitted
  • output (DOM)
  • props pass to subcomponents
  • Internal state (data, computed properties, methods)
  • πŸ‘‰ if public API doesn't change, we should refactor component without changing tests!

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 bullshit

Code coverage bullshit

High coverage !== bug proof

πŸ’© Code coverage bullshit

cc-side-effect

πŸ’© Code coverage bullshit


function foo(a, unitTest = false) {
  // code

  if (weirdAndHardToConditionToTest || unitTest) {
    // code
  }

  return bar;
}
    

😳 Testing privates functions

...

⏱ Be careful with metrics

cc is good but is JUST one metric.

πŸ“Έ 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

Example in Vue.js

      
import { mount, createLocalVue } from '@vue/test-utils'
import VueRouter from 'vue-router'

const localVue = createLocalVue()

localVue.use(VueRouter)
localVue.use(Vuex)
const router = new VueRouter()

const store = new Vuex.Store({
  state: {},
})

mount(Component, {
  localVue,
  router,
  store,
})
      
    

About: Async tests

      
test('should render an page with data', async () => {
  expect.assertions(2)

  const wrapper = mount(StudentList, {
    localVue,
    router,
    store,
    sync: false,
  })
  await flushPromises()
  
  expect(wrapper.html()).toMatchSnapshot()
  // extra security (avoid empty table in snapshot)
  expect(wrapper.text()).toContain('Ralph Wiggum')
})

      
      
const flushPromises = () => new Promise(resolve => setTimeout(resolve));
      
    

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

Low coverage on your team?

Totem Team

totem

Don't forget...

we write tests to catch bugs πŸ›

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();
// });
      
    

Too many tools?

Who want to hire plumber who only use a screwdriver?

πŸ™

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