跳至内容

从 Vue Test Utils v1 迁移

回顾 VTU v1 -> VTU v2 的更改,以及一些代码片段来展示所需的修改。如果您遇到此处未记录的错误或行为差异,请 打开一个问题.

更改

propsData 现在是 props

在 VTU v1 中,您将使用 propsData 挂载选项传递道具。这令人困惑,因为您在 Vue 组件的 props 选项中声明道具。现在您可以使用 props 挂载选项传递 propspropsData 将继续支持以实现向后兼容。

之前:

js
const App = {
  props: ['foo']
}

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

之后:

js
const App = {
  props: ['foo']
}

const wrapper = mount(App, {
  props: {
    foo: 'bar'
  }
}

不再有 createLocalVue

在 Vue 2 中,插件通常会修改全局 Vue 实例并将各种方法附加到原型。从 Vue 3 开始,情况不再如此 - 您使用 createApp 而不是 new Vue 创建一个新的 Vue 应用程序,并使用 createApp(App).use(/* ... */) 安装插件。

为了避免在 Vue Test Utils v1 中污染全局 Vue 实例,我们提供了 createLocalVue 函数和 localVue 挂载选项。这将使您能够为每个测试拥有一个隔离的 Vue 实例,避免跨测试污染。在 Vue 3 中,这不再是一个问题,因为插件、mixin 等不会修改全局 Vue 实例。

对于大多数以前使用 createLocalVuelocalVue 挂载选项来安装插件、mixin 或指令的情况,您现在可以使用 global 挂载选项。以下是一个使用 localVue 的组件和测试示例,以及它现在的样子(使用 global.plugins,因为 Vuex 是一个插件)

之前:

js
import Vuex from 'vuex'
import { createLocalVue, mount } from '@vue/test-utils'

const App = {
  computed: {
    count() {
      return this.$state.count
    }
  }
}

const localVue = createLocalVue()
localVue.use(Vuex)
const store = new Vuex.Store({
  state: {
    return { count: 1 }
  }
})

const wrapper = mount(App, {
  store
  localVue
})

之后:

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

const App = {
  computed: {
    count() {
      return this.$state.count
    }
  }
}

const store = createStore({
  state() {
    return { count: 1 }
  }
})

const wrapper = mount(App, {
  global: {
    plugins: [store]
  }
})

mocksstubs 现在位于 global

mocksstubs 应用于所有组件,而不仅仅是您传递给 mount 的组件。为了反映这一点,mocksstubs 位于新的 global 挂载选项中

之前:

js
const $route = {
  params: {
    id: '1'
  }
}

const wrapper = mount(App, {
  stubs: {
    Foo: true
  },
  mocks: {
    $route
  }
}

之后:

js
const $route = {
  params: {
    id: '1'
  }
}

const wrapper = mount(App, {
  global: {
    stubs: {
      Foo: true
    },
    mocks: {
      $route
    }
  }
}

shallowMountrenderStubDefaultSlot

shallowMount 旨在存根任何自定义组件。虽然这在 Vue Test Utils v1 中是这种情况,但存根组件仍然会渲染它们的默认 <slot />。虽然这是无意的,但一些用户开始喜欢这个功能。此行为在 v2 中得到纠正 - 存根组件的插槽内容不会被渲染

给定此代码

js
import { shallowMount } from '@vue/test-utils'

const Foo = {
  template: `<div><slot /></div>`
}

const App = {
  components: { Foo },
  template: `
    <div>
      <Foo>
        Foo Slot
      </Foo>
    </div>
  `
}

之前:

js
describe('App', () => {
  it('renders', () => {
    const wrapper = shallowMount(App)
    console.log(wrapper.html())
    // renders:
    // <div>
    //   <foo-stub>
    //     Foo Slot
    //   </foo-stub>
    // </div>
  })
})

之后:

js
describe('App', () => {
  it('renders', () => {
    const wrapper = shallowMount(App)
    console.log(wrapper.html())
    // renders:
    // <div>
    //   <foo-stub>
    //   </foo-stub>
    // </div>
  })
})

您可以像这样启用旧的行为

js
import { config } from '@vue/test-utils'

config.global.renderStubDefaultSlot = true

destroy 现在是 unmount 以匹配 Vue 3

Vue 3 将 vm.$destroy 重命名为 vm.$unmount。Vue Test Utils 也随之改变;wrapper.destroy() 现在是 wrapper.unmount()

scopedSlots 现在与 slots 合并

Vue 3 将 slotscoped-slot 语法统一到一个语法下,即 v-slot,您可以在 文档 中阅读。由于 slotscoped-slot 现在已合并,因此 scopedSlots 挂载选项现在已弃用 - 只需对所有内容使用 slots 挂载选项即可。

slots 的范围现在公开为 params

当使用字符串模板作为插槽内容时,如果未使用包装的 <template #slot-name="scopeVar"> 标签明确定义,则插槽范围在评估插槽时将作为 params 对象提供。

差异
shallowMount(Component, {
-  scopedSlots: {
+  slots: {
-    default: '<p>{{props.index}},{{props.text}}</p>'
+    default: '<p>{{params.index}},{{params.text}}</p>'
  }
})

findAll().at() 已删除

findAll() 现在返回一个 DOMWrapper 数组。

之前

js
wrapper.findAll('[data-test="token"]').at(0);

之后

js
wrapper.findAll('[data-test="token"]')[0];

createWrapper() 已删除

createWrapper() 现在仅是内部函数,不能再导入。如果您需要访问不是 Vue 组件的 DOM 元素的包装器,可以使用 new DOMWrapper() 构造函数。

之前

js
import { createWrapper } from "@vue/test-utils";

describe('App', () => {
  it('renders', () => {
    const body = createWrapper(document.body);
    expect(body.exists()).toBe(true);
  })

之后

js
import { DOMWrapper } from "@vue/test-utils";

describe('App', () => {
  it('renders', () => {
    const body = new DOMWrapper(document.body);
    expect(body.exists()).toBe(true);
  })

测试运行器升级说明

Vue Test Utils 与框架无关 - 您可以将其与您喜欢的任何测试运行器一起使用。

此语句是 @vue/test-utils 的核心。但我们确实认识到,在某些情况下,将代码库和相应的测试套件迁移到 vue@3 可能是一项相当大的工作。

本节尝试汇集我们的社区在进行迁移以及将底层测试运行堆栈更新到更现代版本时发现的一些常见问题。这些与 @vue/test-utils 无关,但我们希望它能帮助您完成这个重要的迁移步骤。

@vue/vue3-jest + jest@^28

如果您决定抓住机会将测试运行器工具升级到更现代的版本,请牢记以下几点。

ReferenceError: Vue is not defined vue-jest#479

当使用 jest-environment-jsdom 包时,它默认从 package.json browser 条目 加载库。您可以覆盖它以使用 node 导入,并修复此错误

js
// jest.config.js
module.exports = {
  testEnvironmentOptions: {
    customExportConditions: ["node", "node-addons"],
  }
}

快照现在包含我的注释节点

如果您使用快照测试并且注释节点泄漏到您的快照中,请注意,comments 现在始终 保留,并且仅在生产环境中删除。您可以通过调整 app.config.compilerOptions 来覆盖此行为,以从快照中删除它们

  • 通过 vue-jest 配置.
    js
    // jest.config.js
    module.exports = {
      globals: {
        'vue-jest': {
          compilerOptions: {
            comments: false
          }
        }
      }
    }
  • 通过 @vue/test-utils mountingOptions.global.config,全局或在每个测试的基础上。

与 v1 的比较

这是针对从 VTU 1 迁移过来的用户的表格,比较了这两个 API。

基本 API

导出说明
enableAutoDestroyenableAutoUnmount 替换

挂载选项

选项说明
mocks嵌套在 global
propsData现在称为 props
provide嵌套在 global
mixins嵌套在 global
plugins嵌套在 global
component嵌套在 global
directives嵌套在 global
attachToDocument重命名为 attachTo。参见 此处
scopedSlots已删除。ScopedSlots 在 Vue 3 中与 slots 合并
context已删除。与 Vue 2 不同,不再有意义。
localVue已删除。不再需要 - 在 Vue 3 中没有全局 Vue 实例需要修改。
listeners已删除。在 Vue 3 中不再存在
parentComponent已删除

包装器 API (挂载)

方法说明
find仅支持 querySelector 语法。使用 findComponent(Comp) 查找 Vue 组件
setValue适用于选择框、复选框、单选按钮、输入框、文本区域。返回 nextTick
trigger返回 nextTick。你可以执行 await wrapper.find('button').trigger('click')
destroy重命名为 unmount 以匹配 Vue 3 生命周期钩子名称。
contains已移除。使用 find
emittedByOrder已移除。使用 emitted
setSelected已移除。现在是 setValue 的一部分
setChecked已移除。现在是 setValue 的一部分
is已删除
isEmpty已移除。使用匹配器,例如 this
isVueInstance已删除
name已删除
setMethods已删除