如何编写模拟 vue 组件中 $route 对象的测试
How to write test that mocks the $route object in vue components
我有一个包含像 this.$route.fullPath
这样的语句的组件,如果我想测试该组件,我应该如何模拟 fullPath
of $route
对象的值?
最好不要模拟 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()
})
})
})
注意事项:
- 我使用的是仅运行时版本的 vue,因此
render: h => h('router-view')
。
- 我只测试
totest
组件,但如果 totest
引用其他组件,则可能需要其他组件,例如。 another_component
在这个例子中。
- 您需要
nextTick
渲染 HTML 才能查看 it/test 它。
其中一个问题是我发现的大多数示例都引用了 vue-router
的旧版本,参见 the migrations docs,例如。一些示例使用 router.go()
现在不起作用。
感谢@SColvin 的回答;在我的场景中帮助找到了答案,其中我有一个带有 router-link 的组件正在抛出
ERROR: '[Vue warn]: Error in render function: (found in <RouterLink>)'
在单元测试期间,因为 Vue 没有提供路由器。使用@SColvin answer 重写最初由 vue-cli 从
提供的测试
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 别人!
我不同意最佳答案 - 你可以毫无问题地模拟 $route
。
另一方面,在基础构造器上多次安装 vue-router 会 给你带来问题。它添加 $route
和 $router
作为只读属性。这使得在以后的测试中无法覆盖它们。
vue-test-utils有两种实现方法。
使用 mocks option
模拟 vue-router
const $route = {
fullPath: 'full/path'
}
const wrapper = mount(ComponentWithRouter, {
mocks: {
$route
}
})
wrapper.vm.$route.fullPath // 'full/path'
您也可以使用 createLocalVue 安全地安装 Vue Router:
的测试中安全安装 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')
除了@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。
看看这个使用 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>`
)
})
})
这就是我一直在做的 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');
});
您可以通过设置 vm._routerRoot._router
模拟到 vm.$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
我发现的最简单的方法是模拟 $route。
it('renders $router.name', () => {
const $route = {
name: 'test name - avoriaz'
}
const wrapper = shallow(Component, {
mocks: {
$route
}
})
expect(wrapper.text()).to.equal($route.name)
})
我发现最简单的方法是使用 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);
});
});
希望对您有所帮助!!!
没有答案能帮到我,所以我深入研究了 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
});
})
})
你可以很容易地通过router和shallow mount,它不会给你报错。如果你想通过你使用的商店:
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
✔
您不必专门 "mock" 路由器。你的应用程序可以在全局 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
})
上面的想法是我需要在 SomeVueComponent.vue
为 created/mounted 之前更改 $route
状态。假设您可以创建包装器并希望根据其他状态或操作测试组件 this.$router.push('/something')
,您可以随时监视 wrapper.vm
实例
let routerPushSpy = jest.spyOn(wrapper.vm.$router, 'push') // or before hooks, etc
在撰写本文时,似乎存在一个开放的缺陷,它使以下内容无法工作,因为 vm.$route
将始终未定义,使上述成为唯一的选择(据我所知),因为没有其他选择"mock" $route
的方法,因为安装 VueRouter 会将只读属性写入 $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 link 问题的复现:https://github.com/vuejs/vue-test-utils/issues/1136
为什么所有的答案都这么复杂?你可以这样做:
...
wrapper = mount(HappyComponent, {
mocks: {
$route: {fullPath: ''}
},
})
...
我有一个包含像 this.$route.fullPath
这样的语句的组件,如果我想测试该组件,我应该如何模拟 fullPath
of $route
对象的值?
最好不要模拟 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()
})
})
})
注意事项:
- 我使用的是仅运行时版本的 vue,因此
render: h => h('router-view')
。 - 我只测试
totest
组件,但如果totest
引用其他组件,则可能需要其他组件,例如。another_component
在这个例子中。 - 您需要
nextTick
渲染 HTML 才能查看 it/test 它。
其中一个问题是我发现的大多数示例都引用了 vue-router
的旧版本,参见 the migrations docs,例如。一些示例使用 router.go()
现在不起作用。
感谢@SColvin 的回答;在我的场景中帮助找到了答案,其中我有一个带有 router-link 的组件正在抛出
ERROR: '[Vue warn]: Error in render function: (found in <RouterLink>)'
在单元测试期间,因为 Vue 没有提供路由器。使用@SColvin answer 重写最初由 vue-cli 从
提供的测试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 别人!
我不同意最佳答案 - 你可以毫无问题地模拟 $route
。
另一方面,在基础构造器上多次安装 vue-router 会 给你带来问题。它添加 $route
和 $router
作为只读属性。这使得在以后的测试中无法覆盖它们。
vue-test-utils有两种实现方法。
使用 mocks option
模拟 vue-routerconst $route = {
fullPath: 'full/path'
}
const wrapper = mount(ComponentWithRouter, {
mocks: {
$route
}
})
wrapper.vm.$route.fullPath // 'full/path'
您也可以使用 createLocalVue 安全地安装 Vue Router:
的测试中安全安装 vue-routerconst 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')
除了@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。
看看这个使用 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>`
)
})
})
这就是我一直在做的 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');
});
您可以通过设置 vm._routerRoot._router
模拟到 vm.$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
我发现的最简单的方法是模拟 $route。
it('renders $router.name', () => {
const $route = {
name: 'test name - avoriaz'
}
const wrapper = shallow(Component, {
mocks: {
$route
}
})
expect(wrapper.text()).to.equal($route.name)
})
我发现最简单的方法是使用 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);
});
});
希望对您有所帮助!!!
没有答案能帮到我,所以我深入研究了 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
});
})
})
你可以很容易地通过router和shallow mount,它不会给你报错。如果你想通过你使用的商店:
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
✔
您不必专门 "mock" 路由器。你的应用程序可以在全局 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
})
上面的想法是我需要在 SomeVueComponent.vue
为 created/mounted 之前更改 $route
状态。假设您可以创建包装器并希望根据其他状态或操作测试组件 this.$router.push('/something')
,您可以随时监视 wrapper.vm
实例
let routerPushSpy = jest.spyOn(wrapper.vm.$router, 'push') // or before hooks, etc
在撰写本文时,似乎存在一个开放的缺陷,它使以下内容无法工作,因为 vm.$route
将始终未定义,使上述成为唯一的选择(据我所知),因为没有其他选择"mock" $route
的方法,因为安装 VueRouter 会将只读属性写入 $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 link 问题的复现:https://github.com/vuejs/vue-test-utils/issues/1136
为什么所有的答案都这么复杂?你可以这样做:
...
wrapper = mount(HappyComponent, {
mocks: {
$route: {fullPath: ''}
},
})
...