The official Vue.JS testing library and based on Avoriaz, vue-test-utils, is just around the corner. It’s a great tool as it provides all necessary tooling for making quick to write unit tests in a VueJS application.
Jest, developed by Facebook, is a great companion testing framework which makes testing your programs a breeze.
Below are just a few of its awesome features:
Below we’ll explore just how easy testing becomes with these tools by building and testing a Vue project step-by-step.
By the end, you’ll see why so many developers have thrown away traditional testing strategies in favor of Jest.
Here’s what we’ll go through today:
Learn all you need to start testing Vue components quickly and easily.
Testing Vue.js Components with Jest
Let’s start by creating a new project using vue-cli
. When prompted, answer “NO” to all yes/no questions.
npm install -g vue-clivue init webpack vue-testcd vue-test
Then, we’ll need to install some dependencies.
# Install dependenciesnpm i -D jest vue-jest babel-jest
jest-vue-preprocessor
allows Jest to understand .vue
files, and babel-jest
for the integration with the transcompiler software, Babel.
Next, install vue-test-utils
from the package manager, npm
.
npm i -D vue-test-utils
Now that we have all our tools working, add the following Jest configuration in the package.json
.
..."jest": {"moduleNameMapper": {"^vue$": "vue/dist/vue.common.js"},"moduleFileExtensions": ["js","vue"],"transform": {"^.+\\.js$": "<rootDir>/node_modules/babel-jest",".*\\.(vue)$": "<rootDir>/node_modules/vue-jest"}}...
The moduleFileExtensions
will tell Jest which extensions to look for, and transform
determines which preprocessor to use for a file extension.
Note on Single File Restriction
I’ll be using Single File Components for this example, if split to seperate files results may vary.
Below, we’ll begin adding a test
script to our package.json
.
To add our test, first create a MessageList.vue
component under src/components
.
<template><ul><li v-for="message in messages">{{ message }}</li></ul></template><script>export default {name: 'list',props: ['messages']}</script>
Then update your App.vue
to look like so. This will complete our program so we can move on to a test folder.
<template><div id="app"><MessageList :messages="messages"/></div></template><script>import MessageList from './components/MessageList'export default {name: 'app',data: () => ({ messages: ['Hey John', 'Howdy Paco'] }),components: {MessageList}}</script>
We have already a couple of components that we can test. Let’s create a test
folder under the project root, and a App.test.js
.
import Vue from "vue";import App from "../src/App";describe("App.test.js", () => {let cmp, vm;beforeEach(() => {cmp = Vue.extend(App); // Create a copy of the original componentvm = new cmp({data: {// Replace data value with this fake datamessages: ["Cat"]}}).$mount(); // Instances and mounts the component});it('equals messages to ["Cat"]', () => {expect(vm.messages).toEqual(["Cat"]);});});
Right now, if we run npm test
(or npm t
as a shorthand version), the test should run and pass. Since we’re modifying the tests, let’s better run it in watch mode:
npm t -- --watch
Perfect the Vue testing skills sought after by recruiters and managers alike. With Educative’s interactive, developer-authored courses, you can get hands on experience with testing techniques used by industry developers today.
While a great start, this test is currently too simple.
Let’s change our rest to check that the output is the expected as well. For that we can use the amazing Snapshots feature of Jest, that will generate a snapshot of the output and check it against in the upcoming runs.
Add code below after the previous it
in your App.test.js
.
it("has the expected html structure", () => {expect(vm.$el).toMatchSnapshot();});
Running the test now will create a test/__snapshots__/App.test.js.snap
file.
Let’s open it and inspect it.
// Jest Snapshot v1, https://goo.gl/fbAQLPexports[`App.test.js has the expected html structure 1`] = `<divid="app"><ul><li>Cat</li></ul></div>`;
Another good step forward, but there is a big problem here: the MessageList
component has been rendered as well.
This demonstrates a key takeaway: unit tests must be tested as an independent unit.
In other words, in App.test.js
we want to test the App
component and don’t care at all about anything else.
This can lead to several problems. Imagine for example, that the children components (MessageList
in this case) performed side effect operations on the created
hook, such as calling fetch
, which causes state changes.
This would mean that our test would alter our component to succeed, breaking repeat executions.
Luckily, Shallow Rendering solves this nicely.
Shallow Rendering is a technique that assures your component is rendered without children. This is useful for:
Shallow rendering works by stubbing the
components
key, therender
method and the lifecycle hooks, all behind the scenes.
vue-test-utils
provide us with Shallow Rendering among other features. Look below to see how we can apply shallow rendering in our test script.
import { shallowMount } from "@vue/test-utils";import App from "../src/App";describe("App.test.js", () => {let cmp;beforeEach(() => {cmp = shallowMount(App, {// Create a shallow instance of the componentdata: {messages: ["Cat"]}});});it('equals messages to ["Cat"]', () => {// Within cmp.vm, we can access all Vue instance methodsexpect(cmp.vm.messages).toEqual(["Cat"]);});it("has the expected html structure", () => {expect(cmp.element).toMatchSnapshot();});});
When running Jest in watch mode, you’ll see the test passes, but the Snapshot doesn’t match.
Press u
to regenerate the snapshot.
Open and inspect it again:
// Jest Snapshot v1, https://goo.gl/fbAQLPexports[`App.test.js has the expected html structure 1`] = `<divid="app"><!-- --></div>`;
Notice that no children components were generated. This means that we successfully tested the App
component fully isolated from the component tree. We can now test our app without fear of calling or altering any of our other components.
To accomplish a similar test with shallow rendering, you can implement the MessageList.test.js
test as follows.
import { mount } from "@vue/test-utils";import MessageList from "../src/components/MessageList";describe("MessageList.test.js", () => {let cmp;beforeEach(() => {cmp = mount(MessageList, {// Be aware that props is overridden using `propsData`propsData: {messages: ["Cat"]}});});it('has received ["Cat"] as the message property', () => {expect(cmp.vm.messages).toEqual(["Cat"]);});it("has the expected html structure", () => {expect(cmp.element).toMatchSnapshot();});});
To see other time saving tips and walkthroughs on testing with Jest, see Educative’s course Testing Vue Components with Jest.
You’ll build on what you learned here today, reviewing some expert Jest techniques and completing more hands-on walkthroughs of testing various types of components.
By the end, you’ll be a Vue and Jest expert able to build professional, comprehensive test scripts for any components used in modern JavaScript programs.
Free Resources