Vue-test-utils
Vue-test-utils 是官方提供的内置在vue中的一个测试工具,它可以加载某个vue实例并可以提取,替换其中的方法,属性,事件等。
其实熟悉了Vue框架之后,再来看vue-test-util工具会很容易,因为二者之间的许多概念是相同的。
这篇博客基于vue-test-utils,vue的官方文档,对于部分官方文档中有歧义的地方进行了尝试,也参考了一些博客之后进行了解释。主要是用于概念的理解以及基本的几个用法的解释。
API
Mount
参数
- component: Component
- options: Options
返回值:Wrapper
options
context:Object
将上下文传递给函数式组件,该选项只能用于函数式组件
示例:
1
2
3
4
5
6
7
8
9import Foo from './Foo.vue'
import Bar from './Bar.vue'
const wrapper = mount(Component, {
context: {
props: { show: true },
children: [Foo, Bar]
}
})
expect(wrapper.is(Component)).toBe(true)slots: Array
|Component|string 为组件提供一个 slot 内容的对象。该对象中的键名就是相应的 slot 名,键值可以是一个组件、一个组件数组、一个字符串模板或文本。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import Foo from './Foo.vue'
const bazComponent = {
name: 'baz-component',
template: '<p>baz</p>'
}
const wrapper = shallowMount(Component, {
slots: {
default: [Foo, '<my-component />', 'text'],
fooBar: Foo, // 将会匹配 `<slot name="FooBar" />`.
foo: '<div />',
bar: 'bar',
baz: bazComponent,
qux: '<my-component />'
}
})
expect(wrapper.find('div')).toBe(true)这里官方文档的描述太过简单,可能会产生歧义,如果大家对这段内容有疑惑,可以查看这篇文章
scopedSlots
提供一个该组件所有作用域插槽的对象。每个键对应到插槽的名字。
你可以使用 slot-scope 特性设置 prop 的名称:
1
2
3
4
5shallowMount(Component, {
scopedSlots: {
foo: '<p slot-scope="foo">{{foo.index}},{{foo.text}}</p>'
}
})否则插槽被计算的时候可以通过
props
对象使用 prop:1
2
3
4
5shallowMount(Component, {
scopedSlots: {
default: '<p>{{props.index}},{{props.text}}</p>'
}
})你也可以传递一个函数将 prop 作为参数带入:
1
2
3
4
5
6
7shallowMount(Component, {
scopedSlots: {
foo: function(props) {
return this.$createElement('div', props.index)
}
}
})或者你可以使用 JSX。如果你在一个方法里撰写 JSX,babel-plugin-transform-vue-jsx 会自动注入
this.$createElement
:1
2
3
4
5
6
7shallowMount(Component, {
scopedSlots: {
foo(props) {
return <div>{props.text}</div>
}
}
})stubs:
{ [name: string]: Component | boolean } | Array
将子组件存根。可以是一个要存根的组件名的数组或对象。如果
stubs
是一个数组,则每个存根都是一个<${component name}-stub>
。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import Foo from './Foo.vue'
mount(Component, {
stubs: ['registered-component']
})
shallowMount(Component, {
stubs: {
// 使用一个特定的实现作为存根
'registered-component': Foo,
// 使用创建默认的实现作为存根。
// 这里默认存根的组件名是 `another-component`。
// 默认存根是 `<${the component name of default stub}-stub>`。
'another-component': true
}
})mocks: Object
为实例添加额外的属性。在伪造全局注入的时候有用。
示例:
1
2
3
4
5
6
7const $route = { path: 'http://www.example-path.com' }
const wrapper = shallowMount(Component, {
mocks: {
$route
}
})
expect(wrapper.vm.$route.path).toBe($route.path)localVue: Vue
通过 [
./createLocalVue.md
] 创建的一个Vue
的本地拷贝,用于挂载该组件的时候。在这份拷贝上安装插件可以防止原始的Vue
被污染。示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import { createLocalVue, mount } from '@vue/test-utils'
import VueRouter from 'vue-router'
import Foo from './Foo.vue'
const localVue = createLocalVue()
localVue.use(VueRouter)
const routes = [{ path: '/foo', component: Foo }]
const router = new VueRouter({
routes
})
const wrapper = mount(Component, {
localVue,
router
})
expect(wrapper.vm.$route).toBeInstanceOf(Object)attachToDocument: Boolean
当设为
true
时,组件在渲染时将会挂载到 DOM 上。如果添加到了 DOM 上,你应该在测试的最后调用
wrapper.destroy()
将元素从文档中移除并销毁组件实例。attrs: Object
设置组件实例的
$attrs
对象。propsData
在组件被挂载时设置组件实例的 prop。
示例:
1
2
3
4
5
6
7
8
9
10const Component = {
template: '<div>{{ msg }}</div>',
props: ['msg']
}
const wrapper = mount(Component, {
propsData: {
msg: 'aBC'
}
})
expect(wrapper.text()).toBe('aBC')提示
值得注意的是
propsData
实际上是一个 Vue API,不是 Vue Test Utils 的挂载选项。它会被extends
处理。请查阅其它选项。listeners: Object
设置组件实例的
$listeners
对象。parentComponent: Object
用来作为被挂载组件的父级组件。
示例:
1
2
3
4
5
6import Foo from './Foo.vue'
const wrapper = shallowMount(Component, {
parentComponent: Foo
})
expect(wrapper.vm.$parent.$options.name).toBe('foo')provide: Object
为组件传递用于注入的属性。可查阅 provie/inject 了解更多。
示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14const Component = {
inject: ['foo'],
template: '<div>{{this.foo()}}</div>'
}
const wrapper = shallowMount(Component, {
provide: {
foo() {
return 'fooValue'
}
}
})
expect(wrapper.text()).toBe('fooValue')其它选项
当
mount
和shallowMount
的选项包含了挂载选项之外的选项时,则会将它们通过扩展覆写到其组件选项。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23const Component = {
template: '<div>{{ foo() }}{{ bar() }}{{ baz() }}</div>',
methods: {
foo() {
return 'a'
},
bar() {
return 'b'
}
}
}
const options = {
methods: {
bar() {
return 'B'
},
baz() {
return 'C'
}
}
}
const wrapper = mount(Component, options)
expect(wrapper.text()).toBe('aBC')
ShallowMount
用法与mount相同,只不过会将所有子组件给stub,类似mount的options中的stub属性把所有子组件填写后的效果
render
将一个对象渲染成为一个字符串并返回一个 cheerio 包裹器。
Cheerio 是一个类似 jQuery 的库,可以在 Node.js 中游览 DOM 对象。它的 API 和 Vue Test Utils 的 Wrapper
类似。
render
在底层使用 vue-server-renderer
将一个组件渲染为静态的 HTML。
render
被包含在了 @vue/server-test-utils
包之中。
renderToString
将一个组件渲染为 HTML。
renderToString
在底层使用 vue-server-renderer
将一个组件渲染为 HTML。
selector
很多方法的参数中都包含选择器。一个选择器可以是一个 CSS 选择器、一个 Vue 组件或是一个查找选项对象
createLocalVue
- 返回值:
{Component}
- 用法:
createLocalVue
返回一个 Vue 的类供你添加组件、混入和安装插件而不会污染全局的 Vue 类。
可通过 options.localVue
来使用:
1 | import { createLocalVue, shallowMount } from '@vue/test-utils' |
createWrapper(node [, options])
参数:
{vm|HTMLElement} node
{Object} options
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- `{Boolean} attachedToDocument`
- **返回值:**
- `{Wrapper}`
- **用法:**
`createWrapper` 为一个被挂载的 Vue 实例或一个 HTML 元素创建一个 `Wrapper`。
```js
import { createWrapper } from '@vue/test-utils'
import Foo from './Foo.vue'
const Constructor = Vue.extend(Foo)
const vm = new Constructor().$mount()
const wrapper = createWrapper(vm)
expect(wrapper.vm.foo).toBe(true)
Wrapper
上述的API都是为了创建一个vue实例的wrapper,那么创建完成之后,这个wrapper自身也是有很多接口的。
具体就在这里不重复了,大致的作用类似与options对wrapper的配置,wrapper的方法可以在wrapper实例产生之后去更改和获取某些属性,如setMethods可以改变vue中的methods,trigger可以触发某个组件上的指定事件。
这里我们就举个例子通过不同的方式来测试vue中的methods来说明它的用法:
第一种就是官方文档的示例
1
2
3
4
5
6
7
8
9
10import { mount } from '@vue/test-utils'
import sinon from 'sinon'
import Foo from './Foo.vue'
const wrapper = mount(Foo)
const clickMethodStub = sinon.stub()
wrapper.setMethods({ clickMethod: clickMethodStub })
wrapper.find('button').trigger('click')
expect(clickMethodStub.called).toBe(true)这里要注意,不同版本的setMethods不太一样,有的版本它是一个同步方法,佐伊上述写法是对的,但是有的版本是异步方法,就需要等待它生效才可以,如:
1
2
3
4
5
6
7
8
9
10
11
12import { mount } from '@vue/test-utils'
import sinon from 'sinon'
import Foo from './Foo.vue'
import Vue from 'vue';
const wrapper = mount(Foo)
const clickMethodStub = sinon.stub()
wrapper.setMethods({ clickMethod: clickMethodStub })
await Vue.nextTick();
wrapper.find('button').trigger('click')
expect(clickMethodStub.called).toBe(true)第二种方法是在创建wrapper时传入一个options,这里利用的是mount会将options中的其他选项通过extend扩展如wrapper中的特性,具体代码可以参考options中的其他选项部分的代码。
上述两种方式都可以结合sinon去mock一个vue实例中的方法用于测试。
当然也有其他的方式可以传入一个方法进入wrapper中,如options中的context和provide等。