Skip to content

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)