Unit testing Vue components

Edd Yerburgh

twitter @eddyerburgh

github.com/eddyerburgh

Why write unit tests?

My first React job...

Fixing bugs!

Unit tests to the rescue

Unit test benefits

  • Check components work correctly
  • Provide documentation
  • Easier debugging
  • Less bugs 

How?

// sum.js
function sum(a, b) {
  return a + b;
}
module.exports = sum;
// sum.spec.js
const sum = require('./sum');

test('returns the sum of its input', () => {
  expect(sum(1, 3)).toBe(4);
});

A unit test

Passing tests

A failing test

Vue components

Vue single file component (SFCs)

<template>
  <div>
    <p>Counter: {{counter}}</p>
    <button @click="counter++">Add 1</button>
  </div>
</template>

<script>
export default {
  name: 'counter',
  data: () => ({
    counter: 0
  })
}
</script>

What to test?

Component contract

 

defines the expected behavior of your component and what assumptions are reasonable to make about its usage

Trigger an input

 Expect an output

Input

  • User action
  • Props

Output

  • Rendered output
  • Vue events
  • Function calls (Vuex dispatch)

⚠️

Avoid testing implementation detail 

Counter spec

  • increments counter when button is clicked

How?

Vue SFC

<template>
  <div>
    <p>Counter: {{counter}}</p>
    <button @click="counter++">Add 1</button>
  </div>
</template>

<script>
export default {
  name: 'counter',
  data: () => ({
    counter: 0
  })
}
</script>

compiled SFC

module.exports = {
  render:function (){
    var _vm=this;
    var _h=_vm.$createElement;
    var _c=_vm._self._c||_h; 
    return _c('div', [_c('p', [_vm._v("Counter: " + _vm._s(_vm.counter))]), _vm._v(" "), _c('button', {
      on: {
        "click": _vm.counter++    
      }  
    }, [_vm._v("Add 1")])])
  },
  staticRenderFns: [],
  name: 'counter',
  props: ['title'],
  data: () => {
    counter: 0
  }
}

Compilation process

Compilers

  • Webpack (vue-loader)
  • Jest (vue-jest)
  • Browserify (vueify)

github.com/eddyerburgh/vue-unit-test-perf-comparison

Test runner 10 100 1000 5000
tape 2.32s 3.49s 9.28s 38s
jest 2.44s 4.50s 21.84s 91s
mocha-webpack 2.32s 3.07s 10.79s 38s
karma-mocha 7.93s 11.01s 33.30s 119s
ava 19.05s 73.44s 625.15s 7161s

Test runner comparison

Use Jest or mocha-webpack

https://vue-test-utils.vuejs.org/en/guides/testing-SFCs-with-jest.html

https://vue-test-utils.vuejs.org/en/guides/testing-SFCs-with-mocha-webpack.html

vue-test-utils

Vue testing library

mount

returns a running instance of component inside a wrapper

Needs to run in a browser environment

(JSDOM 👌)

import Counter from '@/components/Counter'
import { mount } from 'vue-test-utils'

describe('Counter.vue', () => {
  it('increments counter on click', () => {
    const wrapper = mount(Counter)
    expect(wrapper.find('span').text()).toBe('0')
    wrapper.find('button').trigger('click')
    expect(wrapper.find('span').text()).toBe('1')
  })
})

Testing a component

mount

shallow

shallow

import { shallow } from 'vue-test-utils'
import Counter from '~/components/Counter'

test('increments counter when button is clicked', () => {
  const wrapper = shallow(Counter)

  expect(wrapper.text()).toContain('0')

  wrapper.find('button').trigger('click')

  expect(wrapper.text()).toContain('1')
})

Using shallow

We've written some tests!

Testing Vue.js applications out now

manning.com/books/testing-vuejs-applications

Unit Testing Vue Components

By Edd Yerburgh

Unit Testing Vue Components

The how, why, and what of unit testing components

  • 1,733