How to write test that mocks the $route object in

2020-05-18 11:10发布

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

11条回答
Lonely孤独者°
2楼-- · 2020-05-18 11:31

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!

查看更多
Rolldiameter
3楼-- · 2020-05-18 11:34

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')
查看更多
爷、活的狠高调
4楼-- · 2020-05-18 11:39

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.

查看更多
小情绪 Triste *
5楼-- · 2020-05-18 11:39

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)
})
查看更多
The star\"
6楼-- · 2020-05-18 11:41

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.

查看更多
我欲成王,谁敢阻挡
7楼-- · 2020-05-18 11:47

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>`
    )
  })
})
查看更多
登录 后发表回答