web-dev-qa-db-ja.com

vue componentsで$ routeオブジェクトをモックするテストを書く方法

this.$route.fullPathのようなステートメントを含むコンポーネントがあります。そのコンポーネントをテストする場合、fullPathof $routeオブジェクトの値をどのようにモックする必要がありますか?

24
Jerry Zhang

_vue-router_をモックするのではなく、それを使用してコンポーネントをレンダリングし、適切に機能するルーターを取得することをお勧めします。例:

_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()
    })
  })
})
_

注意事項:

  1. ランタイム専用バージョンのvueを使用しているため、render: h => h('router-view')です。
  2. 私はtotestコンポーネントのみをテストしていますが、totestで参照されている場合は他のコンポーネントが必要になる場合があります。この例では_another_component_。
  3. HTMLを表示/テストする前に、HTMLをレンダリングするにはnextTickが必要です。

問題の1つは、私が見つけた例のほとんどが_vue-router_の古いバージョンを参照していることです。たとえば、 移行ドキュメント 一部の例ではrouter.go()を使用していますが、現在は機能しません。

32
SColvin

トップの答えに同意しません-$routeを問題なくモックできます。

一方、ベースコンストラクターにvue-routerを複数回インストールするとwillが問題になります。 $routeおよび$routerを読み取り専用プロパティとして追加します。これにより、将来のテストでそれらを上書きすることができなくなります。

vue-test-utils でこれを達成するには2つの方法があります。

mocksオプションでvue-routerをモックする

const $route = {
    fullPath: 'full/path'
}
const wrapper = mount(ComponentWithRouter, { 
  mocks: {
    $route
  } 
})

wrapper.vm.$route.fullPath // 'full/path'

CreateLocalVueを使用してVue Routerを安全にインストールすることもできます。

createLocalVueを使用したテストでのvue-routerの安全なインストール

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

答えは私を助けてくれなかったので、私はvue-test-utilsドキュメントを掘り下げ、自分自身が有効な答えを見つけたので、インポートする必要があります。

import { shallowMount,createLocalVue } from '@vue/test-utils';
import router from '@/router.ts';
const localVue = createLocalVue();

サンプルvueインスタンスを作成しました。テスト中はshallowMountを使用して、vueアプリインスタンスとルーターを提供する必要があります。

describe('Components', () => {
  it('renders a comment form', () => {
    const COMMENTFORM = shallowMount(CommentForm,{
      localVue,
      router
    });
  })
})

簡単にルーターを浅いマウントに渡すことができ、エラーは発生しません。あなたが使用する店を渡したい場合:

import { shallowMount,createLocalVue } from '@vue/test-utils';
import router from '@/router.ts';
import store from '@/store.ts';
const localVue = createLocalVue();

そして、ストアを渡します:

describe('Components', () => {
  it('renders a comment form', () => {
    const COMMENTFORM = shallowMount(CommentForm,{
      localVue,
      router,
      store
    });
  })
})

このソリューションは、次のエラーを解決しました。

  • this.$route.params.idを使用すると、未定義のプロパティ 'params'を読み取れません
  • 不明なカスタム要素router-link

3
Ilyas karim

彼の答えに対して@SColvinに感謝します。私のシナリオで答えを見つけるのを助けました

ERROR: '[Vue warn]: Error in render function: (found in <RouterLink>)'

Vueにはルーターが提供されていなかったため、ユニットテスト中。

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

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

ビューにパラメーターを渡す必要がないので、コンポーネントをデフォルトのレンダリングとして単純化でき、プッシュする必要もnextTickを待つ必要もありません。 HTH他の誰か!

1
9swampy

私が見つけた最も簡単な方法は、localVueを使用することです

import { createLocalVue, mount } from '@vue/test-utils'
import ComponentName from 'componentPath'
import Vuex from 'vuex'
import store from '@/store/store' //Add store file if any getters is accessed
import VueRouter from 'vue-router'

describe('File name', () => { 
const localVue = createLocalVue()
localVue.use(VueRouter)
const routes = [  //Can also be rreplaced with route(router.js) file
    {
        path: '/path',
        component: ComponentName,
        name: 'Route name'
    }
]
const router = new VueRouter({
    routes
})
router.Push({ 
              name: 'Route name',
              params: {} 
            }) //if needed
const wrapper = mount(ComponentName, {localVue, router, store })
beforeEach(function() {      
});

    it('Method()', () => {
        wrapper.vm.methodName()
        expect(wrapper.vm.$route.path).toBe(routes[0].path)
    });
});

それが役に立てば幸い!!!

1

これは、私が この記事 に従って行ってきたことです。

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');
});
0
d512

Vue-test-utilsを使用してこの例を見てみましょう。ここでは、ルーターとストアの両方をモックしています。

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>`
    )
  })
})
0
Daniel Kmak

特にルーターを「モック」する必要はありません。アプリケーションは、グローバルvueスコープでVueRouterを設定できますが、テストで問題なく実行することができます。

VueRouterを使用してlocalVueの使用方法を読み取ります: https://vue-test-utils.vuejs.org/guides/#using-with-vue-router

現在、メインアプリから複雑なルーターを取得し、jest.spyOn()router.Push()に呼び出し、一部のコンポーネントでshallowMount()を実行してコンポーネントを作成する前にパスを設定できますcreated()フックでのルート処理。

回避策

// 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
})

上記は、$routeが作成/マウントされる前にSomeVueComponent.vue状態を変更する必要があるという考えで行われます。ラッパーを作成でき、他の状態またはアクションに基づいてコンポーネントthis.$router.Push('/something')をテストしたい場合は、wrapper.vmインスタンスを常にスパイできます。

let routerPushSpy = jest.spyOn(wrapper.vm.$router, 'Push') // or before hooks, etc

この記事の執筆時点では、vm.$routeは常に未定義であるため、以下を動作させない未解決の欠陥があるようです。 VueRouterをインストールすると、読み取り専用プロパティが$routeに書き込まれるため、$route

Vue-test-utilsドキュメントから 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

ここに興味がある場合は、問題の再現へのgithubリンクがあります: https://github.com/vuejs/vue-test-utils/issues/1136

0
steven87vt

@SColvinからのすばらしい答えに加えて、 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
  })
})

これも vue-test-utils で動作するはずだと思います。

0
Franey

vm。$ routerにモックできますvm._routerRoot._router

例えば

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!

ここでソースコードを再確認できます: https://github.com/vuejs/vue-router/blob/dev/dist/vue-router.js#L558

0
nhanbach

私が見つけた最も簡単な方法は、$ routeをモックすることです。

it('renders $router.name', () => {
  const $route = {
    name: 'test name - avoriaz'
  }


 const wrapper = shallow(Component, {
    mocks: {
      $route
    }
  })
  expect(wrapper.text()).to.equal($route.name)
})
0
Evan Burbidge