Table of Contents
URL: https://www.progressiverobot.com/vuejs-unit-testing-karma-mocha/
At some point, any serious development project should implement testing for their components. Generally, the first step is <^>unit testing<^>. Unit testing allows you to ensure that the behavior of your individual components is reliable and consistent. By using <^>Karma<^> and <^>Mocha<^>, combined with <^>Webpack<^>, we can unit-test Vue components with relative ease.
Installation
There's no soft way to put it, the JavaScript web app testing scene is a complicated beast. As a result, the configuration required for a successful unit-testing setup is fairly extensive. Accordingly, you'll probably be best off using vue-cli with the webpack template (<^>$ vue init webpack my-project<^>) and testing enabled.
Even then, there are some configuration changes to make to <^>test/unit/karma.conf.js<^>. You'll need to specify the plugins you're using, and possibly change the launcher. In this case, I'm using <^>karma-chrome-launcher<^> instead of <^>karma-phantomjs-launcher<^>.
[label karma.conf.js]
var webpackConfig = require('../../build/webpack.test.conf');
module.exports = function (config) {
config.set({
// To run in additional browsers:
// 1. install corresponding karma launcher
// http://karma-runner.github.io/0.13/config/browsers.html
// 2. add it to the `browsers` array below.
browsers: ['Chrome'],
frameworks: ['mocha', 'sinon-chai'],
reporters: ['spec', 'coverage'],
files: ['./index.js'],
preprocessors: {
'./index.js': ['webpack', 'sourcemap']
},
// ** ADD THIS IN ** (vue-cli's webpack template doesn't add it by default)
plugins: [
// Launchers
'karma-chrome-launcher',
// Test Libraries
'karma-mocha',
'karma-sinon-chai',
// Preprocessors
'karma-webpack',
'karma-sourcemap-loader',
// Reporters
'karma-spec-reporter',
'karma-coverage'
],
webpack: webpackConfig,
webpackMiddleware: {
noInfo: true
},
coverageReporter: {
dir: './coverage',
reporters: [
{ type: 'lcov', subdir: '.' },
{ type: 'text-summary' }
]
}
})
}
Your First Component Unit Test
Let's create a small component to test.
[label TestMe.vue]
<template>
<p>{{propValue}}</p>
</template>
<script>
export default {
props: ['propValue']
}
</script>
Now we'll add a spec for it in <^>test/unit/specs<^>. This just checks that the component's text is set to the property value.
[label specs/TestMe.spec.js]
import Vue from 'vue';
// The path is relative to the project root.
import TestMe from 'src/components/TestMe';
describe('TestMe.vue', () => {
it(`should render propValue as its text content`, () => {
// Extend the component to get the constructor, which we can then initialize directly.
const Constructor = Vue.extend(TestMe);
const comp = new Constructor({
propsData: {
// Props are passed in "propsData".
propValue: 'Test Text'
}
}).$mount();
expect(comp.$el.textContent)
.to.equal('Test Text');
});
});
Waiting For Async Updates
Vue updates the DOM asynchronously, in ticks. Therefore, when we modify anything that affects the DOM, we need to wait for the DOM to update using <^>Vue.nextTick()<^> before making any assertions.
[label TestMe2.vue]
<template>
<p>{{dataProp}}</p>
</template>
<script>
export default {
data() {
return {
dataProp: 'Data Text'
}
}
}
</script>
[label specs/TestMe2.spec.js]
import Vue from 'vue';
// The path is relative to the project root.
import TestMe2 from 'src/components/TestMe2';
describe('TestMe2.vue', () => {
...
it(`should update when dataText is changed.`, done => {
const Constructor = Vue.extend(TestMe2);
const comp = new Constructor().$mount();
comp.dataProp = 'New Text';
Vue.nextTick(() => {
expect(comp.$el.textContent)
.to.equal('New Text');
// Since we're doing this asynchronously, we need to call done() to tell Mocha that we've finished the test.
done();
});
});
});
Reference
Hopefully that helps get you started!
However, the way components are instanced and extended in Vue can be a bit confusing, so you may want to take a look at the official Vue tests to get a better idea on how to test various component capabilities.