You’re browsing the documentation for Vue Test Utils for Vue v2.x and earlier.

To read docs for Vue Test Utils for Vue 3, click here.

Wrapper

Vue Test Utils 是一个基于包裹器的 API。

Wrapper 是一个对象,该对象包含了一个挂载的组件或 vnode,以及测试该组件或 vnode 的方法。

属性

vm

Component (只读):这是该 Vue 实例。你可以通过 wrapper.vm 访问一个实例所有的方法和属性。这只存在于 Vue 组件包裹器或绑定了 Vue 组件包裹器的 HTMLElement 中。

element

HTMLElement (只读):包裹器的根 DOM 节点

options

options.attachedToDocument

Boolean (只读):如果组件在渲染之后被添加到了文档上则为 true

selector

Selector: 被 find()findAll() 创建这个 wrapper 时使用的选择器。

方法

attributes

返回 Wrapper DOM 节点的特性对象。如果提供了 key,则返回这个 key 对应的值。

  • 参数:

    • {string} key 可选的
  • 返回值:{[attribute: string]: any} | string

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.attributes().id).toBe('foo')
expect(wrapper.attributes('id')).toBe('foo')

classes

返回 Wrapper DOM 节点的 class。

返回 class 名称的数组。或在提供 class 名的时候返回一个布尔值。

  • 参数:

    • {string} className 可选的
  • 返回值:Array<{string}> | boolean

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.classes()).toContain('bar')
expect(wrapper.classes('bar')).toBe(true)

contains

判断 Wrapper 是否包含了一个匹配选择器的元素或组件。

  • 参数:

    • {string|Component} selector
  • 返回值: {boolean}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const wrapper = mount(Foo)
expect(wrapper.contains('p')).toBe(true)
expect(wrapper.contains(Bar)).toBe(true)

destroy

销毁一个 Vue 组件实例。

  • 示例:
import { mount } from '@vue/test-utils'
import sinon from 'sinon'

const spy = sinon.stub()
mount({
  render: null,
  destroyed() {
    spy()
  }
}).destroy()
expect(spy.calledOnce).toBe(true)

如果挂载时 attachToattachToDocument 选项导致组件被挂载到文档,则组件的 DOM 元素也将会从文档中被移除。

对于函数式组件来说,destroy 只会从文档中移除渲染出来的 DOM 元素。

emitted

返回一个包含由 Wrapper vm 触发的自定义事件的对象。

  • 返回值:{ [name: string]: Array<Array<any>> }

  • 示例:

import { mount } from '@vue/test-utils'

test('emit demo', async () => {
  const wrapper = mount(Component)

  wrapper.vm.$emit('foo')
  wrapper.vm.$emit('foo', 123)

  await wrapper.vm.$nextTick() // 等待事件处理完成

  /*
  wrapper.emitted() 返回如下对象:
  {
    foo: [[], [123]]
  }
  */

  // 断言事件已经被触发
  expect(wrapper.emitted().foo).toBeTruthy()

  // 断言事件的数量
  expect(wrapper.emitted().foo.length).toBe(2)

  // 断言事件的有效数据
  expect(wrapper.emitted().foo[1]).toEqual([123])
})

你也可以把上面的代码写成这样:

// 断言事件已经被触发
expect(wrapper.emitted('foo')).toBeTruthy()

// 断言事件的数量
expect(wrapper.emitted('foo').length).toBe(2)

// 断言事件的有效数据
expect(wrapper.emitted('foo')[1]).toEqual([123])

.emitted() 方法每次被调用时都返回相同的对象,而不是返回一个新的,所以当新事件被触发时该对象会被更新:

const emitted = wrapper.emitted()

expect(emitted.foo.length).toBe(1)

// 想办法让 `wrapper` 触发 "foo" 事件

expect(emitted.foo.length).toBe(2)

emittedByOrder

废弃警告

emittedByOrder 已经被废弃并会在未来的发布中被移除。

请换用 wrapper.emitted

返回一个包含由 Wrapper vm 触发的自定义事件的数组。

  • 返回值:Array<{ name: string, args: Array<any> }>

  • 示例:

import { mount } from '@vue/test-utils'

const wrapper = mount(Component)

wrapper.vm.$emit('foo')
wrapper.vm.$emit('bar', 123)

/*
`wrapper.emittedByOrder() 返回如下数组:
[
  { name: 'foo', args: [] },
  { name: 'bar', args: [123] }
]
*/

// 断言事件的触发顺序
expect(wrapper.emittedByOrder().map(e => e.name)).toEqual(['foo', 'bar'])

exists

断言 WrapperWrapperArray 是否存在。

如果被一个空 WrapperWrapperArray 调用则返回 false

  • 返回值:{boolean}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.exists()).toBe(true)
expect(wrapper.find('does-not-exist').exists()).toBe(false)
expect(wrapper.findAll('div').exists()).toBe(true)
expect(wrapper.findAll('does-not-exist').exists()).toBe(false)

find

废弃警告

使用 find 搜索组件的方式已经被废弃并会被移除。请换用 findComponent

返回匹配选择器的第一个 DOM 节点或 Vue 组件的 Wrapper

可以使用任何有效的 DOM 选择器 (使用 querySelector 语法)。

  • 参数:

    • {string} selector
  • 返回值:{Wrapper}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const wrapper = mount(Foo)

const div = wrapper.find('div')
expect(div.exists()).toBe(true)

const byId = wrapper.find('#bar')
expect(byId.element.id).toBe('bar')
  • 注意:

    • 你可以链式调用 find
const button = wrapper.find({ ref: 'testButton' })
expect(button.find('.icon').exists()).toBe(true)
  • 延伸阅读:get

findAll

废弃警告

使用 findAll 搜索组件的方式已经被废弃并会被移除。请换用 findAllComponents

返回一个 WrapperArray

可以使用任何有效的选择器

  • 参数:

    • {string|Component} selector
  • 返回值:{WrapperArray}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const wrapper = mount(Foo)

const div = wrapper.findAll('div').at(0)
expect(div.is('div')).toBe(true)

const bar = wrapper.findAll(Bar).at(0) // 已废弃的用法
expect(bar.is(Bar)).toBe(true)

findComponent

返回第一个匹配的 Vue 组件的 Wrapper

  • 参数:

    • {Component|ref|name} selector
  • 返回值: {Wrapper}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const wrapper = mount(Foo)

const bar = wrapper.findComponent(Bar) // => 通过组件实例找到 Bar
expect(bar.exists()).toBe(true)
const barByName = wrapper.findComponent({ name: 'bar' }) // => 通过 `name` 找到 Bar
expect(barByName.exists()).toBe(true)
const barRef = wrapper.findComponent({ ref: 'bar' }) // => 通过 `ref` 找到 Bar
expect(barRef.exists()).toBe(true)

findAllComponents

为所有匹配的 Vue 组件返回一个 WrapperArray

  • 参数:

    • {Component|ref|name} selector
  • 返回值: {WrapperArray}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'
import Bar from './Bar.vue'

const wrapper = mount(Foo)
const bar = wrapper.findAllComponents(Bar).at(0)
expect(bar.exists()).toBeTruthy()
const bars = wrapper.findAllComponents(Bar)
expect(bars).toHaveLength(1)

html

返回 Wrapper DOM 节点的 HTML 字符串。

  • 返回值:{string}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.html()).toBe('<div><p>Foo</p></div>')

get

废弃警告

使用 get 搜索组件的方式已经被废弃并会被移除。请换用 getComponent

find 工作起来一样,但是如果未匹配到给定的选择器时会抛出错误。当搜索一个可能不存在的元素时你应该使用 find。当获取一个应该存在的元素时你应该使用这个方法,并且如果没有找到的话它会提供一则友好的错误信息。

import { mount } from '@vue/test-utils'

const wrapper = mount(Foo)

// 和 `wrapper.find` 相似。
// 如果 `get` 没有找到任何元素将会抛出一个而错误。`find` 则不会做任何事。
expect(wrapper.get('.does-exist'))

expect(() => wrapper.get('.does-not-exist'))
  .to.throw()
  .with.property(
    'message',
    'Unable to find .does-not-exist within: <div>the actual DOM here...</div>'
  )

is

废弃警告

使用 is 断言 DOM 结点或 vm 匹配选择器的方式已经被废弃并会被移除。

可以考虑一个诸如 jest-dom 提供的自定义匹配。或为 DOM 元素类型断言换用原生的 Element.tagName

为了保留这些测试,一个有效替换:

  • is('DOM_SELECTOR') 的方式是一个 wrapper.element.tagName 的断言。
  • is('ATTR_NAME') 的方式是一个 wrapper.attributes('ATTR_NAME') 的 truthy 断言。
  • is('CLASS_NAME') 的方式是一个 wrapper.classes('CLASS_NAME') 的 truthy 断言。

当使用 findComponent 时,通过 findComponent(Comp).element 访问 DOM 元素。

断言 Wrapper DOM 节点或 vm 匹配选择器

  • 参数:

    • {string|Component} selector
  • 返回值:{boolean}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.is('div')).toBe(true)

isEmpty

废弃警告

isEmpty 已经被废弃并会在未来的发布中被移除。

可以考虑一个诸如 jest-dom 中提供的自定义匹配。

当使用 findComponent 时,可以通过 findComponent(Comp).element 访问其 DOM 元素。

断言 Wrapper 并不包含子节点。

  • 返回值:{boolean}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.isEmpty()).toBe(true)

isVisible

断言 Wrapper 是否可见。

如果有一个祖先元素拥有 display: nonevisibility: hidden 样式则返回 false

这可以用于断言一个组件是否被 v-show 所隐藏。

  • 返回值: {boolean}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.isVisible()).toBe(true)
expect(wrapper.find('.is-not-visible').isVisible()).toBe(false)

isVueInstance

废弃警告

isVueInstance 已经被废弃并会在未来的发布中被移除。

依赖 isVueInstance 断言的测试并没有特别大的意义,我们建议将与之相关的断言替换为更有目的性的断言。

为了保留这些测试,一个替换 isVueInstance() 的可行方式是对 wrapper.find(...).vm 的 truthy 断言。

断言 Wrapper 是 Vue 实例。

  • 返回值:{boolean}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.isVueInstance()).toBe(true)

name

废弃警告

name 已经被废弃并会在未来的发布中被移除。

如果 Wrapper 包含一个 Vue 实例则返回组件名,否则返回 Wrapper DOM 节点的标签名。

  • 返回值:{string}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.name()).toBe('Foo')
const p = wrapper.find('p')
expect(p.name()).toBe('p')

props

返回 Wrapper vm 的 props 对象。如果提供了 key,则返回这个 key 对应的值。

注意:该包裹器必须包含一个 Vue 实例。

  • 参数:

    • {string} key 可选的
  • 返回值:{[prop: string]: any} | any

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo, {
  propsData: {
    bar: 'baz'
  }
})
expect(wrapper.props().bar).toBe('baz')
expect(wrapper.props('bar')).toBe('baz')

setChecked

设置 checkbox 或 radio 类 <input> 元素的 checked 值并更新 v-model 绑定的数据。

  • 参数:

    • {Boolean} checked (默认值:true)
  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

test('setChecked demo', async () => {
  const wrapper = mount(Foo)
  const radioInput = wrapper.find('input[type="radio"]')

  await radioInput.setChecked()

  expect(radioInput.element.checked).toBeTruthy()
})
  • 注意:

当你尝试通过 radioInput.element.checked = true; radioInput.trigger('input') 经由 v-model 向 state 设置值的时候,v-model 不会被触发。v-model 是被 change 事件触发的。

checkboxInput.setChecked(checked) 是接下来这段代码的别名。

checkboxInput.element.checked = checked
checkboxInput.trigger('click')
checkboxInput.trigger('change')

setData

设置 Wrapper vm 的属性。

setData 通过递归调用 Vue.set 生效。

注意:该包裹器必须包含一个 Vue 示例。

  • 参数:

    • {Object} data
  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

test('setData demo', async () => {
  const wrapper = mount(Foo)

  await wrapper.setData({ foo: 'bar' })

  expect(wrapper.vm.foo).toBe('bar')
})

setMethods

废弃警告

setMethods 已经被废弃并会在未来的发布中被移除。

这里没有明确的路径替换 setMethods,因为这取决于你之前的用法。这很容易导致依赖实现细节的琐碎的测试,而这是不推荐的

我们建议重新考虑那些测试。

如果是为了存根一个复杂方法,可将其从组件中提取出来并单独测试。如果是为了断言一个方法被调用,可使用你的测试运行器窥探它。

设置 Wrapper vm 的方法并强制更新。

注意:该包裹器必须包含一个 Vue 示例。

  • 参数:

    • {Object} methods
  • 示例:

import { 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)

setProps

  • 参数:

    • {Object} props
  • 用法:

设置 Wrapper vm 的 prop 并强制更新。

注意:该包裹器必须包含一个 Vue 示例。

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

test('setProps demo', async () => {
  const wrapper = mount(Foo)

  await wrapper.setProps({ foo: 'bar' })

  expect(wrapper.vm.foo).toBe('bar')
})

你也可以传递一个 propsData 对象,这会用该对象来初始化 Vue 示例。

// Foo.vue
export default {
  props: {
    foo: {
      type: String,
      required: true
    }
  }
}
import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo, {
  propsData: {
    foo: 'bar'
  }
})

expect(wrapper.vm.foo).toBe('bar')

setSelected

选择一个 option 元素并更新 v-model 绑定的数据。

  • 示例:
import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

test('setSelected demo', async () => {
  const wrapper = mount(Foo)
  const options = wrapper.find('select').findAll('option')

  await options.at(1).setSelected()

  expect(wrapper.find('option:checked').element.value).toBe('bar')
})
  • 注意:

当你尝试通过 option.element.selected = true; parentSelect.trigger('input') 经由 v-model 向 state 设置值的时候,v-model 不会被触发。v-model 是被 change 事件触发的。

option.setSelected() 是接下来这段代码的别名。

option.element.selected = true
parentSelect.trigger('change')

setValue

设置一个文本控件或 select 元素的值并更新 v-model 绑定的数据。

  • 参数:

    • {String} value
  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

test('setValue demo', async () => {
  const wrapper = mount(Foo)

  const textInput = wrapper.find('input[type="text"]')
  await textInput.setValue('some value')

  expect(wrapper.find('input[type="text"]').element.value).toBe('some value')

  const select = wrapper.find('select')
  await select.setValue('option value')

  expect(wrapper.find('select').element.value).toBe('option value')

  // requires <select multiple>
  const multiselect = wrapper.find('select')
  await multiselect.setValue(['value1', 'value3'])

  const selectedOptions = Array.from(multiselect.element.selectedOptions).map(
    o => o.value
  )
  expect(selectedOptions).toEqual(['value1', 'value3'])
})
  • 注意:

    • textInput.setValue(value) 是接下来这段代码的别名。
    textInput.element.value = value
    textInput.trigger('input')
    
    • select.setValue(value) 是接下来这段代码的别名。
    select.element.value = value
    select.trigger('change')
    

text

返回 Wrapper 的文本内容。

  • 返回值:{string}

  • 示例:

import { mount } from '@vue/test-utils'
import Foo from './Foo.vue'

const wrapper = mount(Foo)
expect(wrapper.text()).toBe('bar')

trigger

在该 Wrapper DOM 节点上异步触发一个事件。

trigger 带有一个可选的 options 对象。options 对象内的属性会被添加到事件上。trigger 会返回一个 Promise,当这个 Promise 被解决时,会确保组件已经被更新。

  • 参数:

    • {string} eventType 必填
    • {Object} options 可选
  • 示例:

import { mount } from '@vue/test-utils'
import sinon from 'sinon'
import Foo from './Foo'

test('trigger demo', async () => {
  const clickHandler = sinon.stub()
  const wrapper = mount(Foo, {
    propsData: { clickHandler }
  })

  await wrapper.trigger('click')

  await wrapper.trigger('click', {
    button: 0
  })

  await wrapper.trigger('click', {
    ctrlKey: true // 用于测试 @click.ctrl 处理函数
  })

  expect(clickHandler.called).toBe(true)
})
  • 设置事件目标:

在这背后,trigger 创建了一个 Event 对象并分发到其包裹器的元素上。

我们没有机会编辑 Event 对象的 target 值,所以你无法在选项对象中设置 target

如果想在 target 中添加一个特性,你需要在调用 trigger 之前设置包裹器元素的那个值。你可以通过 element 属性做到这件事。

const input = wrapper.find('input')
input.element.value = 100
input.trigger('click')