Testing Vue Components

Why tests are important?

  • Less debug time
  • Code proven to meet requirements
  • Documentation!
  • Reduce chances of bugs in new / existing features
  • Improve design
  • Reduce fear

The three laws of TDD

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

Demo

Install dependencies

npm init -y
npm install vue
npm install expect jsdom jsdom-global mocha mocha-webpack vue-loader vue-template-compiler vue-test-utils webpack --save-dev

package.json

"scripts": {
  "test": "mocha-webpack --webpack-config webpack.config.js --require test/setup.js test/*.spec.js",
  "watch": "mocha-webpack --webpack-config webpack.config.js --watch --require test/setup.js test/*.spec.js"
}

test/setup.js

require('jsdom-global')();

webpack.config.js

module.exports = {
    module: {
        rules: [
            {
                test: /\.vue$/,
                use: 'vue-loader'
            }
        ]
    }
};

src/components/Counter.vue

<template>
    <div>
        <span class="count" v-text="count"></span>
        <button @click="count++" class="increment">Increment</button>
        <button @click="count--" class="decrement" v-show="count > 0">Decrement</button>
    </div>
</template>

<script>
    export default {
        data() {
            return {
                count: 0
            };
        }
    }
</script>

test/counter.spec.js

import { mount } from 'vue-test-utils';
import Counter from '../src/components/Counter.vue';
import expect from 'expect';

describe ('Counter', () => {
    let wrapper;

    beforeEach(() => {
        wrapper = mount(Counter);
    });

    it('defaults to a count of 0', () => {
        expect(wrapper.vm.count).toBe(0);
    });

    it('increments the count when the increment button is clicked', () => {
        expect(wrapper.vm.count).toBe(0);
        wrapper.find('.increment').trigger('click');
        expect(wrapper.vm.count).toBe(1);
    });

    it('decrements the count when the decrement button is clicked', () => {
        wrapper.setData({ count: 5 });
        wrapper.find('.decrement').trigger('click');
        expect(wrapper.vm.count).toBe(4);
    });

    it('never goes below 0', () => {
        expect(wrapper.vm.count).toBe(0);
        expect(wrapper.find('.decrement').hasStyle('display', 'none')).toBe(true);
        wrapper.find('.increment').trigger('click');
        expect(wrapper.find('.decrement').hasStyle('display', 'none')).toBe(false);
    });

    it('presents the current count', () => {
        expect(wrapper.find('.count').html()).toContain(0);
        wrapper.find('.increment').trigger('click');
        expect(wrapper.find('.count').html()).toContain(1);
    });
});

Resources

fin.

Testing Vue Components

By Swapnil Agarwal