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.
Let's see an example:
Mounted components are returned inside a Wrapper, which exposes methods for querying and interacting with the component under testing.
# Simulating User InteractionLet's imagine a counter component that increments when user clicks the button:
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:
Notice how the test must be async
and that trigger
needs to be awaited. Check out the Testing Asynchronous Behavior guide to understand why this is needed and other things to consider when testing asynchronous scenarios.
Check out our common tips when writing tests.
Alternatively, you can explore the full API.
# Common Tips # Knowing What to TestFor UI components, we don't recommend aiming 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, we recommend writing 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.
This topic is discussed with more details in a great presentation by Matt O'Connell .
# Shallow mountingSometimes, mounting a whole component with all its 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.
Like mount, it creates a Wrapper that contains the mounted and rendered Vue component, but with stubbed child components.
Notice that using shallowMount
will make the component under testing different from the component you run in your application – some of its parts won't be rendered! This is why it is not the suggested way of testing components unless you face performance issues or need to simplify test arrangements.
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, 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.
By default, Vue batches updates to run asynchronously (on the next "tick"). This is to prevent unnecessary DOM re-renders, and watcher computations (see the docs for more details).
This means that you must wait for updates to run after you change a reactive property. You can do that by awaiting mutation methods like trigger
:
Learn more in the Testing Asynchronous Behavior
# Asserting Emitted EventsEach mounted wrapper automatically records all events emitted by the underlying Vue instance. You can retrieve the recorded events using the wrapper.emitted()
method:
You can then make assertions based on these data:
You can also get an Array of the events in their emit order by calling wrapper.emittedByOrder()
.
You can emit a custom event from a child component by accessing the instance.
Component under test
Test
# Manipulating Component StateYou can directly manipulate the state of the component using the setData
or setProps
method on the wrapper:
You can pass props to the component using Vue's built-in propsData
option:
You can also update the props of an already-mounted component with the wrapper.setProps({})
method.
For a full list of options, please see the mount options section of the docs.
# Mocking TransitionsAlthough calling await Vue.nextTick()
works well for most use cases, there are some situations where additional workarounds are required. These issues will be solved before the vue-test-utils
library moves out of beta. One such example is unit testing components with the <transition>
wrapper provided by Vue.
You might want to write a test that verifies that Foo is shown, then when show
is set to false
, Foo is no longer rendered. Such a test could be written as follows:
In practice, although we are calling and awaiting setData
to ensure the DOM is updated, this test fails. This is an ongoing issue related to how Vue implements the <transition>
component, that we would like to solve before version 1.0. For now, there are some workarounds:
transitionStub
helper
This overrides the default behavior of the <transition>
component and renders the children as soon as the relevant boolean condition changes, as opposed to applying CSS classes, which is how Vue's <transition>
component works.
setData
Another alternative is to simply avoid using setData
by writing two tests, with the required setup performed using mount
or shallowMount
options:
Some of the components may rely on features injected by a global plugin or mixin, for example vuex
and vue-router
.
If you are writing tests for components in a specific app, you can setup the same global plugins and mixins once in the entry of your tests. But in some cases, for example testing a generic component suite that may get shared across different apps, it's better to test your components in a more isolated setup, without polluting the global Vue
constructor. We can use the createLocalVue
method to achieve that:
Note some plugins, like Vue Router, add read-only properties to the global Vue constructor. This makes it impossible to reinstall the plugin on a localVue
constructor, or add mocks for these read-only properties
Another strategy for injected props is simply mocking them. You can do that with the mocks
option:
You can override components that are registered globally or locally by using the stubs
option:
Since routing by definition has to do with the overall structure of the application and involves multiple components, it is best tested via integration or end-to-end tests. For individual components that rely on vue-router
features, you can mock them using the techniques mentioned above.
Your test can only detect inline styles when running in jsdom
.
The Wrapper
exposes an async trigger
method. It can be used to trigger DOM events.
You should be aware that the find
method returns a Wrapper
as well. Assuming MyComponent
contains a button, the following code clicks the button.
The trigger
method takes an optional options
object. The properties in the options
object are added to the Event.
Note that target cannot be added in the options
object.
Component under test
Test
# Keyboard ExampleComponent under test
This component allows to increment/decrement the quantity using various keys.
Test
Limitations
A key name after the dot keydown.up
is translated to a keyCode
. This is supported for the following names:
There are two types of asynchronous behavior you will encounter in your tests:
Vue batches pending DOM updates and applies them asynchronously to prevent unnecessary re-renders caused by multiple data mutations.
You can read more about asynchronous updates in the Vue docs
In practice, this means that after mutating a reactive property, to assert that change your test has to wait while Vue is performing updates. One way is to use await Vue.nextTick()
, but an easier and cleaner way is to just await
the method that you mutated the state with, like trigger
.
Awaiting the trigger above is the same as doing:
Methods that can be awaited are:
# Asynchronous behavior outside of VueOne of the most common asynchronous behaviors outside of Vue is API calls in Vuex actions. The following examples shows how to test a method that makes an API call. This example uses Jest to run the test and to mock the HTTP library axios
. More about Jest manual mocks can be found here .
The implementation of the axios
mock looks like this:
The below component makes an API call when a button is clicked, then assigns the response to value
.
A test can be written like this:
This test currently fails because the assertion is called before the promise in fetchResults
resolves. Most unit test libraries provide a callback to let the runner know when the test is complete. Jest and Mocha both use done
. We can use done
in combination with $nextTick
or setTimeout
to ensure any promises are settled before the assertion is made.
The reason setTimeout
allows the test to pass is because the microtask queue where promise callbacks are processed runs before the task queue, where setTimeout
callbacks are processed. This means by the time the setTimeout
callback runs, any promise callbacks on the microtask queue will have been executed. $nextTick
on the other hand schedules a microtask, but since the microtask queue is processed first-in-first-out that also guarantees the promise callback has been executed by the time the assertion is made. See here for a more detailed explanation.
Another solution is to use an async
function and a package like flush-promises . flush-promises
flushes all pending resolved promise handlers. You can await
the call of flushPromises
to flush pending promises and improve the readability of your test.
The updated test looks like this:
This same technique can be applied to Vuex actions, which return a promise by default.
# Why not justawait button.trigger()
?
As explained above, there is a difference between the time it takes for Vue to update its components, and the time it takes for a Promise, like the one from axios
to resolve.
A nice rule to follow is to always await
on mutations like trigger
or setProps
. If your code relies on something async, like calling axios
, add an await to the flushPromises
call as well.
An example project for this setup is available on GitHub .
TypeScript is a popular superset of JavaScript that adds types and classes on top of regular JS. Vue Test Utils includes types in the distributed package, so it works well with TypeScript.
In this guide, we'll walk through how to setup a testing setup for a TypeScript project using Jest and Vue Test Utils from a basic Vue CLI TypeScript setup.
# Adding TypeScriptFirst you need to create a project. If you don't have Vue CLI installed, install it globally:
And create a project by running:
In the CLI prompt, choose to Manually select features
, select TypeScript, and press enter. This will create a project with TypeScript already configured.
The next step is to add Jest to the project.
# Setting up JestJest 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 .
Install Jest and Vue Test Utils:
Next define a test:unit
script in package.json
.
To teach Jest how to process *.vue
files, we need to install and configure the vue-jest
preprocessor:
Next, create a jest
block in package.json
:
In order to use TypeScript files in tests, we need to set up Jest to compile TypeScript. For that we need to install ts-jest
:
Next, we need to tell Jest to process TypeScript test files with ts-jest
by adding an entry under jest.transform
in package.json
:
By default, Jest will recursively pick up all files that have a .spec.js
or .test.js
extension in the entire project.
To run test files with a .ts
extension, we need to change the testRegex
in the config section in the package.json
file.
Add the following to the jest
field in package.json
:
Jest recommends creating a __tests__
directory right next to the code being tested, but feel free to structure your tests as you see fit. Just beware that Jest would create a __snapshots__
directory next to test files that performs snapshot testing.
Now we've got the project set up, it's time to write a unit test.
Create a src/components/__tests__/HelloWorld.spec.ts
file, and add the following code:
That's all we need to do to get TypeScript and Vue Test Utils working together!
# Resources # Using with Vue Router # Installing Vue Router in testsYou should never install Vue Router on the Vue base constructor in tests. Installing Vue Router adds $route
and $router
as read-only properties on Vue prototype.
To avoid this, we can create a localVue, and install Vue Router on that.
# Testing components that useNote: Installing Vue Router on a
localVue
also adds$route
and$router
as read-only properties to alocalVue
. This means you can not use themocks
option to overwrite$route
and$router
when mounting a component using alocalVue
with Vue Router installed.
router-link
or router-view
When you install Vue Router, the router-link
and router-view
components are registered. This means we can use them anywhere in our application without needing to import them.
When we run tests, we need to make these Vue Router components available to the component we're mounting. There are two methods to do this.
# Using stubs # Installing Vue Router with localVueThe router instance is available to all children components, this is useful for integration level testing.
# Mocking$route
and $router
Sometimes you want to test that a component does something with parameters from the $route
and $router
objects. To do that, you can pass custom mocks to the Vue instance.
# Common gotchasNote: the mocked
$route
and$router
values are not available to children components, either stub this components or use thelocalVue
method.
Installing Vue Router adds $route
and $router
as read-only properties on Vue prototype.
This means any future tests that try to mock $route
or $router
will fail.
To avoid this, never install Vue Router globally when you're running tests; use a localVue
as detailed above.
In this guide, we'll see how to test Vuex in components with Vue Test Utils, and how to approach testing a Vuex store.
# Testing Vuex in components # Mocking ActionsLet’s look at some code.
This is the component we want to test. It calls Vuex actions.
For the purposes of this test, we don’t care what the actions do, or what the store looks like. We just need to know that these actions are being fired when they should, and that they are fired with the expected value.
To test this, we need to pass a mock store to Vue when we shallowMount our component.
Instead of passing the store to the base Vue constructor, we can pass it to a - localVue. A localVue is a scoped Vue constructor that we can make changes to without affecting the global Vue constructor.
Let’s see what this looks like:
What’s happening here? First we tell Vue to use Vuex with the localVue.use
method. This is just a wrapper around Vue.use
.
We then make a mock store by calling new Vuex.Store
with our mock values. We only pass it the actions, since that’s all we care about.
The actions are jest mock functions . These mock functions give us methods to assert whether the actions were called or not.
We can then assert in our tests that the action stub was called when expected.
Now the way we define the store might look a bit foreign to you.
We’re using beforeEach
to ensure we have a clean store before each test. beforeEach
is a mocha hook that’s called before each test. In our test, we are reassigning the store variables value. If we didn’t do this, the mock functions would need to be automatically reset. It also lets us change the state in our tests, without it affecting later tests.
The most important thing to note in this test is that we create a mock Vuex store and then pass it to Vue Test Utils.
Great, so now we can mock actions, let’s look at mocking getters.
# Mocking GettersThis is a fairly simple component. It renders the result of the getters clicks
and inputValue
. Again, we don’t really care about what those getters return – just that their result is being rendered correctly.
Let’s see the test:
This test is similar to our actions test. We create a mock store before each test, pass it as an option when we call shallowMount
, and assert that the value returned by our mock getters is being rendered.
This is great, but what if we want to check our getters are returning the correct part of our state?
# Mocking with ModulesModules are useful for separating out our store into manageable chunks. They also export getters. We can use these in our tests.
Let’s look at our component:
Simple component that includes one action and one getter.
And the test:
# Testing a Vuex StoreThere are two approaches to testing a Vuex store. The first approach is to unit test the getters, mutations, and actions separately. The second approach is to create a store and test against that. We'll look at both approaches.
To see how to test a Vuex store, we're going to create a simple counter store. The store will have an increment
mutation and an evenOrOdd
getter.
Getters, mutations, and actions are all JavaScript functions, so we can test them without using Vue Test Utils and Vuex.
The benefit to testing getters, mutations, and actions separately is that your unit tests are detailed. When they fail, you know exactly what is wrong with your code. The downside is that you will need to mock Vuex functions, like commit
and dispatch
. This can lead to a situation where your unit tests pass, but your production code fails because your mocks are incorrect.
We'll create two test files, mutations.spec.js
and getters.spec.js
:
First, let's test the increment
mutations:
Now let's test the evenOrOdd
getter. We can test it by creating a mock state
, calling the getter with the state
and checking it returns the correct value.
Another approach to testing a Vuex store is to create a running store using the store config.
The benefit of creating a running store instance is we don't have to mock any Vuex functions.
The downside is that when a test breaks, it can be difficult to find where the problem is.
Let's write a test. When we create a store, we'll use localVue
to avoid polluting the Vue base constructor. The test creates a store using the store-config.js
export:
Notice that we use cloneDeep
to clone the store config before creating a store with it. This is because Vuex mutates the options object used to create the store. To make sure we have a clean store in each test, we need to clone the storeConfig
object.
However, cloneDeep
is not "deep" enough to also clone store modules. If your storeConfig
includes modules, you need to pass an object to new Vuex.Store()
, like so:
localVue
createLocalVue
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4