Vue Test Utils
Vue Test Utils (VTU) is a set of utility functions aimed to simplify testing Vue.js components. It provides some methods to mount and interact with Vue components in an isolated manner.
For UI components, it is not recommend to aim for complete line-based coverage, because it leads to too much focus on the internal implementation details of the components and could result in brittle tests.
Instead, it is recommended to write tests that assert your component's public interface, and treat its internals as a black box. A single test case would assert that some input (user interaction or change of props) provided to the component results in the expected output (render result or emitted custom events).
For example, imagine a Counter component which increments a display counter by 1 each time a button is clicked. Its test case would simulate the click and assert that the rendered output has increased by 1. The test should not care about how the Counter increments the value – it only cares about the input and the output.
The benefit of this approach is that as long as your component's public interface remains the same, your tests will pass no matter how the component's internal implementation changes over time.
For example:
Counter.vue
const Counter = {
template: `
<div>
<button @click="count++">Add up</button>
<p>Total clicks: {{ count }}</p>
</div>
`,
data() {
return { count: 0 }
}
}
To simulate the behavior, we need to first locate the button with wrapper.find(), which returns a wrapper for the button element. We can then simulate the click by calling .trigger() on the button wrapper:
test('increments counter value on click', async () => {
const wrapper = mount(Counter)
const button = wrapper.find('button')
const text = wrapper.find('p')
expect(text.text()).toContain('Total clicks: 0')
await button.trigger('click')
expect(text.text()).toContain('Total clicks: 1')
})
Shallow mounting
Sometimes, mounting a whole component with all its all dependencies might become slow or cumbersome. For example, components that contain many child components.
Vue Test Utils allows you to mount a component without rendering its child components (by stubbing them) with the shallowMount method.
import { shallowMount } from '@vue/test-utils'
import Component from '../Component.vue'
const wrapper = shallowMount(Component)
Lifecycle Hooks
When using either the mount
or shallowMount
methods, you can expect your component to respond to all lifecycle events. However, it is important to note that beforeDestroy and destroyed will not be triggered unless the component is manually destroyed using Wrapper.destroy().
Additionally, the component will not be automatically destroyed at the end of each spec (test), and it is up to the user to stub or manually clean up tasks that will continue to run (setInterval or setTimeout, for example) before the end of each spec.
Manipulating Component State
You can directly manipulate the state of the component using the setData
or setProps
method on the wrapper:
wrapper.setData({ count: 10 })
wrapper.setProps({ foo: 'bar' })
Mocking Props
You can pass props to the component using Vue's built-in propsData
option:
import { mount } from '@vue/test-utils'
mount(Component, {
propsData: {
aProp: 'some value'
}
})
Using Vue Test Utils with Jest
Jest is a test runner developed by Facebook, aiming to deliver a battery-included unit testing solution. You can learn more about Jest on its official documentation.
If you are using the Vue CLI to build your project, you can use the plugin cli-plugin-unit-jest to run Jest tests.
After setting up Jest, the first thing to do is to install Vue Test Utils and vue-jest to process Single-File Components:
$ npm install --save-dev @vue/test-utils vue-jest
Then, you need to tell Jest to transform .vue files using vue-jest. You can do so by adding the following configuration in package.json (or in a standalone Jest config file):
package.json
{
"jest": {
"moduleFileExtensions": [
"js",
"json",
// tell Jest to handle `*.vue` files
"vue"
],
"transform": {
// process `*.vue` files with `vue-jest`
".*\\.(vue)$": "vue-jest"
}
}
}
Note
If you are using Babel 7 or higher, you will need to add babel-bridge to your devDependencies ($ npm install --save-dev babel-core@^7.0.0-bridge.0)