How to write test that mocks the $route object in vue components

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')

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!!!


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


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:

  1. I'm using the runtime-only version of vue, hence render: h => h('router-view').
  2. I'm only testing the totest component, but others might be required if they're referenced by totest eg. another_component in this example.
  3. 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.