I have a component that contains statement like this.$route.fullPath
, how should I mock value of fullPath
of $route
object if I want to test that component?

- 16,068
- 14
- 77
- 112

- 1,251
- 2
- 13
- 15
12 Answers
I disagree with the top answer - you can mock $route
without any issue.
On the other hand, installing vue-router multiple times on the base constructor will cause you problems. It adds $route
and $router
as read only properties. Which makes it impossible to overwrite them in future tests.
There are two ways to achieve this with vue-test-utils.
Mocking vue-router with the mocks option
const $route = {
fullPath: 'full/path'
}
const wrapper = mount(ComponentWithRouter, {
mocks: {
$route
}
})
wrapper.vm.$route.fullPath // 'full/path'
You can also install Vue Router safely by using createLocalVue:
Installing vue-router safely in tests with createLocalVue
const localVue = createLocalVue()
localVue.use(VueRouter)
const routes = [
{
path: '/',
component: Component
}
]
const router = new VueRouter({
routes
})
const wrapper = mount(ComponentWithRouter, { localVue, router })
expect(wrapper.vm.$route).to.be.an('object')
-
1First approach doesn't work: `TypeError: Cannot set property $route of #
which has only a getter`. – Daniel Kmak Nov 04 '17 at 13:15 -
Hi Daniel, this error is thrown because you've already installed VueRouter during your tests. Once you call Vue.use(VueRouter), $route and $router are added as read only properties. To be able to modify the $route or $router properties in a test, you must not install Vue Router on the global base constructor. – Edward Nov 04 '17 at 18:52
-
How do you mock $store using the first approach? – d512 Dec 12 '17 at 22:19
-
The same way, but pass $store instead of $route – Edward Dec 13 '17 at 06:59
-
`mocks: { $route }` gives read-only error with or without prior install of VueRouter. – Apr 04 '18 at 04:37
-
I guarantee that you will have installed Vue Router somewhere in your code. Even importing a function from a file that calls Vue.use(VueRouter) will do it. – Edward Apr 04 '18 at 12:51
-
2Worth noting that vue-cli drops a `Vue.use(Router)` in router/index.js, which is loaded into the global Vue. If you have say, a `router.push` call in your component, it is importing that global read-only $router before the test (so it can't be mocked). – Joe Z Apr 06 '18 at 18:04
-
@JoeZoller, That's the problem I'm running into. None of my tests load router, but it's being passed in from router/index.js. So, when I want to test logic that calls $router.push I'm stuck as I am unable to mock or attach a spy. Did you find a way around this? – Nathan Agersea May 18 '18 at 17:57
-
Of note: this only happens when I use beforeEach. I don't have an issue when I setup the wrapper and mocks inside the describe block. – Nathan Agersea May 18 '18 at 18:04
-
@Nathan Agersea No real solution yet. We ended up extracting the routing functionality to a different module for the more interesting code to test. – Joe Z May 30 '18 at 15:10
-
@Edd, this might be a basic question, but if a component uses $router.push(), is it still possible to mock $router? Or in that case is it better to implement an end-to-end test by installing VueRouter (using createLocalVue())? – Matthew White Aug 20 '18 at 07:35
-
1@MatthewWhite you can mock a $route object with a push method and it will work fine! – Edward Aug 20 '18 at 08:09
-
`mocks: {$route}` doesn't work. $route is still empty when the component is created. No errors, it's just that the test fails. `router.push(path)` similarly doesn't work. – mcv Feb 26 '19 at 15:48
-
I'm doing this (setting a prop) in a form pagesource: this.$route.fullPath and I tried the mock method - wrapper.vm.$route is populated in the test but it still bombs out complaining that $route is undefined in the component – Michael Harper Dec 02 '20 at 15:26
Best not mock vue-router
but rather use it to render the component, that way you get a proper working router. Example:
import Vue from 'vue'
import VueRouter from 'vue-router'
import totest from 'src/components/totest'
describe('totest.vue', () => {
it('should totest renders stuff', done => {
Vue.use(VueRouter)
const router = new VueRouter({routes: [
{path: '/totest/:id', name: 'totest', component: totest},
{path: '/wherever', name: 'another_component', component: {render: h => '-'}},
]})
const vm = new Vue({
el: document.createElement('div'),
router: router,
render: h => h('router-view')
})
router.push({name: 'totest', params: {id: 123}})
Vue.nextTick(() => {
console.log('html:', vm.$el)
expect(vm.$el.querySelector('h2').textContent).to.equal('Fred Bloggs')
done()
})
})
})
Things to note:
- I'm using the runtime-only version of vue, hence
render: h => h('router-view')
. - I'm only testing the
totest
component, but others might be required if they're referenced bytotest
eg.another_component
in this example. - You need
nextTick
for the HTML to have rendered before you can look at it/test it.
One of the problems is that most of the examples I found referred to the old version of vue-router
, see the migrations docs, eg. some examples use router.go()
which now doesn't work.
-
This answer saved me a lot of time. However it could use some improvements: If you have multiple tests. you actually have to route to /whatever and then to /totest if you want to reset the state. It would be better to find a way to destroy the router and build it again. When I built my router in each tests, I was getting strange behaviour (it seems like the router state was persisted, and routing to the same "page" did not trigger state changes) – MartinTeeVarga Feb 19 '17 at 22:24
-
[here](https://github.com/tutorcruncher/socket-frontend/blob/master/test/unit/specs/modal.js) is a full test setup which is working. That's probably the clearest approach to describing a full test setup. – SColvin Feb 22 '17 at 17:48
-
Thanks a lot for this example and link to your project repo @SColvin. browsing your specs is helping me a ton, I hope to see and even create my own resources on how to test vue components in the future, it's something there definitely needs to be more of (or at least, more for people who havent had a lot of tdd experience). Thanks again for the great question + excellent answer. – lmiller1990 Feb 26 '17 at 07:54
-
No problem. I agree there aren't as many resources on the subject as I expected. But the most noticeable thing I found from starting with vue and writing unit tests with javascript was **quite how ugly javascript is**. There's a lot of polishing, and shaping and rolling in glitter but it's still fundamentally a shit. I've never appreciated python's elegance, simplicity and sheer communicativeness so much. – SColvin Feb 26 '17 at 14:32
-
1
-
1[here](https://github.com/tutorcruncher/socket-frontend/blob/9140ca968ee7c42e498c25a40c391818c2726c5b/test/unit/specs/modal.js) is the test file as it was back in February. The tests are more complete now but perhaps less easy to get started with. – SColvin Mar 27 '17 at 09:30
-
3I disagree that you shouldn't mock Vue Router. See my answer — https://stackoverflow.com/a/44619365/4939630 – Edward Oct 31 '17 at 19:38
No answer was helping me out, So I dig into vue-test-utils
documentation and found myself a working answer, so you need to import.
import { shallowMount,createLocalVue } from '@vue/test-utils';
import router from '@/router.ts';
const localVue = createLocalVue();
We created a sample vue
instance. While testing you need to use shallowMount
so you can provide vue
app instance and router.
describe('Components', () => {
it('renders a comment form', () => {
const COMMENTFORM = shallowMount(CommentForm,{
localVue,
router
});
})
})
You can easily pass router and to shallow mount and it does not gives you the error. If you want to pass store you use:
import { shallowMount,createLocalVue } from '@vue/test-utils';
import router from '@/router.ts';
import store from '@/store.ts';
const localVue = createLocalVue();
And then pass store:
describe('Components', () => {
it('renders a comment form', () => {
const COMMENTFORM = shallowMount(CommentForm,{
localVue,
router,
store
});
})
})
This solution solved the following errors:
- Cannot read property 'params' of undefined when using
this.$route.params.id
- Unknown custom element
router-link
✔

- 4,592
- 4
- 33
- 47
Easiest method i found is to use localVue
import { createLocalVue, mount } from '@vue/test-utils';
import VueRouter from 'vue-router';
import Vuex from 'vuex';
import ComponentName from '@/components/ComponentName.vue';
// Add store file if any getters is accessed
import store from '@/store/store';
describe('File name', () => {
const localVue = createLocalVue();
localVue.use(VueRouter);
// Can also be replaced with route(router.js) file
const routes = [
{
path: '/path',
component: ComponentName,
name: 'Route name'
}
];
const router = new VueRouter({ routes });
// if needed
router.push({
name: 'Route name',
params: {}
});
const wrapper = mount(ComponentName, {
localVue,
router,
store
});
test('Method()', () => {
wrapper.vm.methodName();
expect(wrapper.vm.$route.path)
.toEqual(routes[0].path);
});
});
Hope it helps!!!

- 1,657
- 17
- 15

- 211
- 1
- 6
Why are all answers so complicated? You can just do:
...
wrapper = mount(HappyComponent, {
mocks: {
$route: { fullPath: '' }
},
})
...

- 16,068
- 14
- 77
- 112
You dont have to specifically "mock" a router. Your application can set VueRouter in the global vue scope and you can still make it do what you want in your tests without issue.
Read the localVue usage with VueRouter
: https://vue-test-utils.vuejs.org/guides/#using-with-vue-router.
I am currently pulling in a complex router from our main app and am able to jest.spyOn()
calls to router.push()
as well as setting the path before the component is created running shallowMount()
for some route handling in a created()
hook.
The Workaround
// someVueComponent.vue
<template>
... something
</template>
<script>
...
data () {
return {
authenticated: false
}
},
...
created () {
if(!this.authenticated && this.$route.path !== '/'){
this.$router.push('/')
}
}
</script>
// someVueComponent.spec.js
import Vuex from 'vuex'
import VueRouter from 'vue-router'
import { shallowMount, createLocalVue } from '@vue/test-utils'
import SomeVueComponent from 'MyApp/components/someVueComponent'
import MyAppRouter from 'MyApp/router'
import MyAppCreateStore from 'MyApp/createStore'
import merge from 'lodash.merge'
function setVueUseValues (localVue) {
localVue.use(Vuex)
localVue.use(VueRouter)
// other things here like custom directives, etc
}
beforeEach(() => {
// reset your localVue reference before each test if you need something reset like a custom directive, etc
localVue = createLocalVue()
setVueUseValues(localVue)
})
let localVue = createLocalVue()
setVueUseValues(localVue)
test('my app does not react to path because its default is "/"', () => {
const options = {
localVue,
router: MyAppRouter,
store: MyAppCreateStore()
}
const routerPushSpy = jest.spyOn(options.router, 'push')
const wrapper = shallowMount(SomeVueComponent, options)
expect(routerPushSpy).toHaveBeenCalledTimes(0)
})
test('my app reacts to path because its not "/" and were not authenticated', () => {
const options = {
localVue,
router: MyAppRouter,
store: MyAppCreateStore()
}
const routerPushSpy = jest.spyOn(options.router, 'push')
options.router.push('/nothomepath')
expect(routerPushSpy).toHaveBeenCalledWith('/nothomepath') // <- SomeVueComponent created hook will have $route === '/nothomepath' as well as fullPath
const wrapper = shallowMount(SomeVueComponent, options)
expect(routerPushSpy).toHaveBeenCalledWith('/') // <- works
})
The above is done with the idea that I need the $route
state changed before SomeVueComponent.vue
is created/mounted. Assuming you can create the wrapper and want to test that the component this.$router.push('/something')
based on some other state or action you can always spy on the wrapper.vm
instance
let routerPushSpy = jest.spyOn(wrapper.vm.$router, 'push') // or before hooks, etc
As of this writing there seems to be an open defect which keeps the following from working because vm.$route
will always be undefined, making the above the only option (that I know of) as there is no other way to "mock" the $route
because installing VueRouter writes read only properties to $route
.
From the vue-test-utils docs https://vue-test-utils.vuejs.org/guides/#mocking-route-and-router:
import { shallowMount } from '@vue/test-utils'
const $route = {
path: '/some/path'
}
const wrapper = shallowMount(Component, {
mocks: {
$route
}
})
wrapper.vm.$route.path // /some/path
If your interested here is the github link to a reproduction of the issue: https://github.com/vuejs/vue-test-utils/issues/1136

- 500
- 7
- 6
All kudos to @SColvin for his answer; helped find an answer in my scenario wherein I had a component with a router-link that was throwing a
ERROR: '[Vue warn]: Error in render function: (found in <RouterLink>)'
during unit test because Vue hadn't been supplied with a router. Using @SColvin answer to rewrite the test originally supplied by vue-cli from
describe('Hello.vue', () =>
{
it('should render correct contents', () =>
{
const Constructor = Vue.extend(Hello);
const vm = new Constructor().$mount();
expect(vm.$el.querySelector('.hello h1').textContent)
.to.equal('Welcome to Your Vue.js App');
});
to
describe('Hello.vue', () =>
{
it('should render correct contents', () =>
{
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: '/', name: 'Hello', component: Hello },
],
});
const vm = new Vue({
el: document.createElement('div'),
/* eslint-disable object-shorthand */
router: router,
render: h => h('router-view'),
});
expect(vm.$el.querySelector('.hello h1').textContent)
.to.equal('Welcome to Your Vue.js App');
});
});
Not needing to pass parameters in to the view I could simplify the component as the default render, no need to push and no need to wait nextTick. HTH someone else!

- 1,441
- 14
- 24
Adding to the great answer from @SColvin, here's an example of this working using Avoriaz:
import { mount } from 'avoriaz'
import Vue from 'vue'
import VueRouter from 'vue-router'
import router from '@/router'
import HappyComponent from '@/components/HappyComponent'
Vue.use(VueRouter)
describe('HappyComponent.vue', () => {
it('renders router links', () => {
wrapper = mount(HappyComponent, {router})
// Write your test
})
})
I believe this should work with vue-test-utils, too.

- 4,164
- 2
- 16
- 18
-
-
4@webdevguy It's a common [Webpack resolve alias](https://webpack.js.org/configuration/resolve/) used by the Vue team as a shortcut to the `src` directory. – Franey Feb 24 '18 at 03:41
Take a look at this example using vue-test-utils, where I'm mocking both router and store.
import ArticleDetails from '@/components/ArticleDetails'
import { mount } from 'vue-test-utils'
import router from '@/router'
describe('ArticleDetails.vue', () => {
it('should display post details', () => {
const POST_MESSAGE = 'Header of our content!'
const EXAMPLE_POST = {
title: 'Title',
date: '6 May 2016',
content: `# ${POST_MESSAGE}`
}
const wrapper = mount(ArticleDetails, {
router,
mocks: {
$store: {
getters: {
getPostById () {
return EXAMPLE_POST
}
}
}
}
})
expect(wrapper.vm.$el.querySelector('h1.post-title').textContent.trim()).to.equal(EXAMPLE_POST.title)
expect(wrapper.vm.$el.querySelector('time').textContent.trim()).to.equal(EXAMPLE_POST.date)
expect(wrapper.vm.$el.querySelector('.post-content').innerHTML.trim()).to.equal(
`<h1>${POST_MESSAGE}</h1>`
)
})
})

- 18,164
- 7
- 66
- 89
This is what I've been doing as per this article:
it('renders $router.name', () => {
const scopedVue = Vue.extend();
const mockRoute = {
name: 'abc'
};
scopedVue.prototype.$route = mockRoute;
const Constructor = scopedVue.extend(Component);
const vm = new Constructor().$mount();
expect(vm.$el.textContent).to.equal('abc');
});

- 32,267
- 28
- 81
- 107
You can mock to vm.$router by setting vm._routerRoot._router
For example
var Constructor = Vue.extend(Your_Component)
var vm = new Constructor().$mount()
var your_mock_router = {hello:'there'}
vm.$router = your_mock_router //An error 'setting a property that has only a getter'
vm._routerRoot._router = your_mock_router //Wow, it works!
You can double check their source code here: https://github.com/vuejs/vue-router/blob/dev/dist/vue-router.js#L558

- 316
- 2
- 2
Easiest way i've found is to mock the $route.
it('renders $router.name', () => {
const $route = {
name: 'test name - avoriaz'
}
const wrapper = shallow(Component, {
mocks: {
$route
}
})
expect(wrapper.text()).to.equal($route.name)
})

- 827
- 9
- 16