Tape is the fastest framework for unit testing Vue components.
In this article, we’ll see how to write Vue unit tests with Tape and Vue Test Utils.
This tutorial is for users familiar with unit testing. If you’re new to unit testing check out unit testing Vue components for beginners.
Tape is a bare bones unit test framework that outputs the test report in TAP (Test Anything Protocol) format.
It’s got a simple API to assert that your JavaScript and Vue components are behaving correctly.
A couple weeks ago I ran some performance tests on different testing frameworks. I wanted to find out which framework was the fastest for testing Vue SFCs (Single File Components).
I added Tape for completeness sake, and was shocked to find it was the fastest performing framework.
To run tests in Tape, we need to do some setup. Let’s dive right in.
I’ve made a simple starter project to start with. Git clone the project into a directory:
git clone https://github.com/eddyerburgh/jest-vue-starter.git
cd
into it and install the dependencies:
cd jest-vue-starter && npm install
Run the dev server as npm run dev
to check out the app.
It’s pretty simple. The the main point of this tutorial is to see how to set up Tape and Vue, not to write complex tests.
First thing to do is install Tape and Vue Test Utils:
npm install --save-dev tape [email protected]
Vue Test Utils is in beta, so we need to request the version explicitly
Vue Test Utils needs a browser environment to run. That doesn’t mean we need to run the tests in a browser (thank God!).
We can use JSDOM to set up a browser environment in Node. It adds global variables like document
and window
to Node.
JSDOM is a bit of a pain to setup. Luckily some enterprising node developer made a wrapper library called browser-env
.
npm install --save-dev browser-env
We need to run browser-env
before the tests. Tape lets us define files to run before the tests, we’ll do that in a sec.
Vue SFCs need to be compiled before they’re tested. We can use **require-hooks**
to run WebPack on files when they’re required in Node. It’s a simple setup.
First, install require-extension-hooks
and its variants:
npm install --save-dev require-extension-hooks require-extension-hooks-babel require-extension-hooks-vue
Let’s make that setup file I spoke about earlier. Create a test
directory, and add a setup.js
file. The final path will be test/setup.js
.
const hooks = require('require-extension-hooks')
// Setup browser environment
require('browser-env')()
// Setup vue files to be processed by `require-extension-hooks-vue`
hooks('vue').plugin('vue').push()
// Setup vue and js files to be processed by `require-extension-hooks-babel`
hooks(['vue', 'js']).plugin('babel').push()
We’re nearly there. Crazy stuff.
Let’s write a smoke test in Tape. Create a new file called List.spec.js
in the test directory. Full path test/List.spec.js
. Copy the test below into the file:
import test from 'tape'
import { shallow } from 'vue-test-utils'
import List from '../../src/components/List.vue'
test('List.vue renders', t => {
t.plan(1)
const wrapper = shallow(List)
t.equal(typeof wrapper, 'object')
})
What’s going on there? We define a test
, and get a t
object in the callback. The t
object contains assertion methods. It also has a plan
method . We need to tell Tape how many tests it should expect.
Now we need a script to run the tests. Open the package.json
and add this script:
"unit": "tape ./test/specs/*.spec.js -r ./test/setup.js"
This tells tape to run all .spec
files in test/specs
. The -r
tells Tape to require
or run test/setup
before running our tests.
Run the unit
tests:
npm run unit
Yay, we have a passing test. But hoo boy—that’s some ugly test output ☹️
Remember I mentioned TAP earlier? This is TAP in it’s naked glory. Pretty ugly right? Don’t worry, we can prettify it.
Install tap-spec
:
npm install --save-dev tap-spec
We can pipe our TAP output from Tape. Edit the unit
script to pipe the output to tap-spec
:
"unit": "tape ./test/specs/*.spec.js -r ./test/setup.js | tap-spec"
And run the tests again:
npm run unit
Much better 👌
Let’s write some tests then. Since we’re using Vue Test Utils, the tests are pretty readable.
In List.spec.js
, we’re going to write a test
that passes an items
array to List
. We’ll use the [shallow](https://github.com/vuejs/vue-test-utils/blob/dev/docs/en/api/shallow.md)
method from Vue Test Utils to shallow mount the component. shallow
returns a wrapper
containing the mounted component. We can use [findAll](https://github.com/vuejs/vue-test-utils/blob/dev/docs/en/api/wrapper/findAll.md)
to search the render tree for<li>
tags, and check how many there are.
Copy the test from below into test/specs/List.spec.js
.
import test from 'tape'
import { shallow } from 'vue-test-utils'
import List from '../../src/components/List'
test('List.vue renders a <li> for each item in props.items', t => {
t.plan(1)
const items = ['', '']
const wrapper = shallow(List, {
propsData: { items }
})
const msg = 'li length matches items.length'
t.equal(wrapper.findAll('li').length, items.length, msg)
})
Watch the tests pass with npm run unit
. Notice we have a custom error message for out t.equals
assertion. The default messages aren’t very readable, so it’s better to add our own.
Now add a new file test/specs/MessageToggle.spec.js
. In here we’ll write a test for, you guessed it, MessageToggle.vue
.
We’re going to write two tests now. One will check the <p>
tag renders a default message. We’ll use shallow
again to get a wrapper containing the mounted component, and use the text
method to return the text inside the <p>
tag.
The second test is similar. We’ll assert that the message changes when the toggle-message
button is pressed. To do that, we can use the [trigger](https://github.com/vuejs/vue-test-utils/blob/dev/docs/en/api/wrapper/trigger.md)
method.
Copy the code below into test/specs/MessageToggle.spec.js
:
import test from 'tape'
import { shallow } from 'vue-test-utils'
import MessageToggle from '../../src/components/MessageToggle'
test('MessageToggle.vue renders default message', t => {
t.plan(1)
const wrapper = shallow(MessageToggle)
t.equal(wrapper.find('p').text(), 'default message', 'has a default message')
})
test('MessageToggle.vue renders default message', t => {
t.plan(2)
const wrapper = shallow(MessageToggle)
const button = wrapper.find('#toggle-message')
const p = wrapper.find('p')
button.trigger('click')
t.equal(p.text(), 'message', 'toggles <p> text to "message"')
button.trigger('click')
t.equal(p.text(), 'toggled message', 'toggles <p> text to "toggled message"')
})
Run the tests again with npm run unit
. Woop—green tests 🙌
Now we’ve added some tests, let’s look at the pros and cons of using Tape.
hasBeenCalledWith
, this is difficult to replicate with t.equal
The pros and cons are pretty similar. Tape is basic, and that can be a good thing or a bad thing depending on who you ask.
Most importantly though, it’s blazing fast!
Fast unit tests are good unit tests.
The best way to work out a new test framework is to use it.
On the next Vue project you start, try Tape. You can find a list of assertions on the Tape README.
Enjoy
You can find the finished repo on GitHub.
**Recommended Courses: **
Vue JS: From Beginner to Professional
☞ https://goo.gl/YYz6TC
Real Time Chat With Laravel Broadcast, Pusher and Vuejs
☞ https://goo.gl/yVkCnd
Vue JS 2.0 - Mastering Web Apps
☞ https://goo.gl/FMpDgr
Learn the core concepts of Vue.js while creating an app
☞ https://goo.gl/dJo2TQ
Vue.js 2 Essentials: Build Your First Vue App
☞ https://goo.gl/NUipkv
☞ JavaScript Programming Tutorial Full Course for Beginners
☞ Learn JavaScript - Become a Zero to Hero
☞ Learn Vue 2 in 65 Minutes -The Vue Tutorial for 2018
☞ Javascript Project Tutorial: Budget App
☞ The Vue Tutorial for 2018 - Learn Vue 2 in 65 Minutes
☞ E-Commerce JavaScript Tutorial - Shopping Cart from Scratch