Skip to main content

2 posts tagged with "vue/test-utils"

View All Tags

· 5 min read
Jeffrey

事件处理

  • Vue 组件通过 props 和通过调用emit. 在本指南中,我们将了解如何使用该emitted()函数验证事件是否正确发出。

计数器组件

这是一个简单的<Counter>组件。它具有一个按钮,当单击该按钮时,会增加一个内部计数变量并发出其值:

<template>
<button @click="handleClick">Increment</button>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const emit = defineEmits(['increment'])
const count = ref(0)
function handleClick() {
count.value += 1
emit('increment', count)
}
</script>

为了全面测试这个组件,我们应该验证是否发出了increment具有最新count值的事件。

断言发出的事件

为此,我们将依赖该emitted()方法。它返回一个对象,其中包含组件已发出的所有事件,以及它们在数组中的参数。让我们看看它是如何工作的:

it('emits an event when clicked', () => {
const wrapper = mount(Counter)

wrapper.find('button').trigger('click')
wrapper.find('button').trigger('click')

expect(wrapper.emitted()).toHaveProperty('increment')
})

首先要注意的是emitted()返回一个对象,其中每个键都匹配一个发出的事件。在这种情况下,increment

这个测试应该通过。我们确保我们发出了一个具有适当名称的事件。

断言事件的论点

这很好——但我们可以做得更好!我们需要检查我们在emit('increment', count)被调用时是否发出了正确的参数。

我们的下一步是断言事件包含该count值。我们通过将参数传递给emitted().

it('emits an event with count when clicked', () => {
const wrapper = mount(Counter)

wrapper.find('button').trigger('click')
wrapper.find('button').trigger('click')

// `emitted()` accepts an argument. It returns an array with all the
// occurrences of `this.$emit('increment')`.
const incrementEvent = wrapper.emitted('increment')

// We have "clicked" twice, so the array of `increment` should
// have two values.
expect(incrementEvent).toHaveLength(2)

// Assert the result of the first click.
// Notice that the value is an array.
expect(incrementEvent[0]).toEqual([1])

// Then, the result of the second one.
expect(incrementEvent[1]).toEqual([2])
})

让我们回顾一下并分解emitted(). 这些键中的每一个都包含测试期间发出的不同值:

// console.log(wrapper.emitted('increment'))
;[
[1], // first time it is called, `count` is 1
[2], // second time it is called, `count` is 2
]

断言复杂事件

想象一下,现在我们的<Counter>组件需要发出一个带有附加信息的对象。例如,我们需要告诉任何监听@increment事件的父组件count是偶数还是奇数:

修改一下我们的点击事件

function handleClick() {
count.value += 1
emit('increment', {
count: count.value,
isEven: count.value % 2 === 0,
})
}

正如我们之前所做的,我们需要在元素上触发click事件。<button>然后,我们emitted('increment')用来确保发出正确的值。

it('emits an event with count when clicked', () => {
const wrapper = mount(Counter)

wrapper.find('button').trigger('click')
wrapper.find('button').trigger('click')

// We have "clicked" twice, so the array of `increment` should
// have two values.
expect(wrapper.emitted('increment')).toHaveLength(2)

// Then, we can make sure each element of `wrapper.emitted('increment')`
// contains an array with the expected object.
expect(wrapper.emitted('increment')[0]).toEqual([
{
count: 1,
isEven: false,
},
])

expect(wrapper.emitted('increment')[1]).toEqual([
{
count: 2,
isEven: true,
},
])
})

测试诸如对象之类的复杂事件负载与测试诸如数字或字符串之类的简单值没有什么不同。

结论

用于emitted()访问从 Vue 组件发出的事件。 emitted(eventName)返回一个数组,其中每个元素代表一个发出的事件。 参数emitted(eventName)[index]以它们发出的相同顺序存储在数组中

· 7 min read
Jeffrey

入门

  • 本教程源码已上传github

什么是 Vue 测试工具?

  • Vue Test Utils (VTU) 是一组实用功能,旨在简化 Vue.js 组件的测试。它提供了一些方法来以隔离的方式挂载和与 Vue 组件交互。 让我们看一个例子:

什么是 Vitest

  • Vitest 是一个由 Vite 提供支持的极速单元测试框架。
  • 你可以在 为什么是 为什么是 Vitest 中了解有关该项目背后的基本原理的更多信息。

vitest 优点

  • 重复使用 Vite 的配置、转换器、解析器和插件 - 在您的应用程序和测试中保持一致。
  • 拥有预期、快照、覆盖等 - 从 Jest 迁移很简单。
  • 智能文件监听模式,就像是测试的 HMR!
  • 由 esbuild 提供的开箱即用 ESM、TypeScript 和 JSX 支持。
import { mount } from '@vue/test-utils'
import { describe, it, expect } from 'vitest'
// 这是测试组件
const MessageComponent = {
template: '<p>{{ msg }}</p>',
props: ['msg'],
}
// 创建测试用例
describe('displays message', () => {
it('displays message', () => {
// 使用 mount函数 挂载组件
const wrapper = mount(MessageComponent, {
props: {
msg: 'Hello world',
},
})
// 断言组件的呈现文本
expect(wrapper.text()).toContain('Hello world') //可通过
expect(wrapper.text()).toContain('Hello') //不可通过
})
})

测试一个 TodoApp

  • 安装组件
  • 查找元素
  • 填写表格
  • 触发事件

入门

TodoApp 我们的一个简单的组件开始: todo 的简单组件

<template>
<div></div>
</template>

<script setup lang="ts" name="TodoApp">
import { ref } from 'vue'
const todos = ref([
{
id: 1,
text: 'Learn Vue.js 3',
completed: false,
},
])
</script>

第一个测试 - 渲染一个 todo

我们将写出验证来做部分的第一个测试。让我们先看看测试然后讨论:

import { mount } from '@vue/test-utils'
import TodoApp from '../TodoApp/index.vue'
import { describe, it, expect } from 'vitest'
describe('renders a todo', () => {
it('renders properly', () => {
const wrapper = mount(TodoApp)
const todo = wrapper.get('[data-test="todo"]')
expect(todo.text()).toBe('Learn Vue.js 3')
})
})
  • 我们从导入 mountVTU 中渲染组件的主要组件。
  • 运行测试会失败,我们通过属性选择器获取元素 [data-test="todo"]

使测试通过

<template>
<div>
<div v-for="todo in todos" :key="todo.id" data-test="todo">
{{ todo.text }}
</div>
</div>
</template>

这个改变,测试就通过了。恭喜!您编写了第一个组件测试。

添加新的待办事项

我们将能够让用户的下一个功能是让用户创建一个新的待办事项。我们需要输入一个输入的表格,供用户输入一些文本。当用户提交表单时,我们新的任务被渲染。我们来看看测试:

import { mount } from '@vue/test-utils'
import TodoApp from '../TodoApp/index.vue'
import { describe, it, expect } from 'vitest'
describe('renders a todo', () => {
it('creates a todo', () => {
const wrapper = mount(TodoApp)
// 通过属性选择器获取全部元素 长度为1
expect(wrapper.findAll('[data-test="todo"]')).toHaveLength(1)
// 设置input元素value = New todo
wrapper.get('[data-test="new-todo"]').setValue('New todo')
// 触发提交事件
wrapper.get('[data-test="form"]').trigger('submit')
// 再次回去长度应为2
expect(wrapper.findAll('[data-test="todo"]')).toHaveLength(2)
})
})

运行这个程序显然会失败。让我们更新 TodoApp.vue 以拥有<form>并测试我们<input>测试,通过:

<template>
<div>
<div v-for="todo in todos" :key="todo.id" data-test="todo">
{{ todo.text }}
</div>
<form data-test="form" @submit.prevent="createTodo">
<input data-test="new-todo" v-model="newTodo" />
<button type="submit"></button>
</form>
</div>
</template>

<script setup lang="ts" name="TodoApp">
import { ref } from 'vue'
const todos = ref([
{
id: 1,
text: 'Learn Vue.js 3',
completed: false,
},
])
const newTodo = ref('')
function createTodo() {
todos.value.push({
id: 2,
text: newTodo.value,
completed: false,
})
}
</script>

我们使用绑定 v-model 到<input>并@submit 监听提交。createTodotodos

这看起来不错,但运行测试显示错误:

待办事项的数量没有。问题是 vitest 以同步方式执行测试, 并可能增加调用最终函数就结束测试。然而,Vue 会异步更新 DOM。我们需要标记测试 async,调用 await 任何导致 DOM 更改的方法。trigger 是这样的方法,所以是 setValue-我们可以按地预先设置并且设置 await 测试应该是一种简单的工作方式:

import { mount } from '@vue/test-utils'
import TodoApp from '../TodoApp/index.vue'
import { describe, it, expect } from 'vitest'
describe('renders a todo', () => {
// 修改测试用例 添加async await
it('creates a todo', async () => {
const wrapper = mount(TodoApp)
expect(wrapper.findAll('[data-test="todo"]')).toHaveLength(1)

await wrapper.get('[data-test="new-todo"]').setValue('New todo')
await wrapper.get('[data-test="form"]').trigger('submit')

// 异步获取长度
await expect(wrapper.findAll('[data-test="todo"]')).toHaveLength(2)
})
})

完成待办事项

现在可以创建待办事项,让我们可以让用户使用如已完成的待办事项为我们/未完成的项目标记。

import { mount } from '@vue/test-utils'
import TodoApp from '../TodoApp/index.vue'
import { describe, it, expect } from 'vitest'
describe('renders a todo', () => {
// 修改测试用例 添加async await
it('completes a todo', async () => {
const wrapper = mount(TodoApp)

await wrapper.get('[data-test="todo-checkbox"]').setValue(true)

expect(wrapper.get('[data-test="todo"]').classes()).toContain('completed')
})
})

本次测试与前两次类似;我们找到一个元素并以相同的方式与之交互(我们 setValue 再次使用,因为我们正在与 a 交互<input>)。

最后,我们做一个断言。我们将为 completed 完成的 todo 应用一个类——然后我们可以使用它来添加一些样式以直观地指示一个 todo 的状态。

我们可以通过更新 todo 元素<template>以包含<input type="checkbox">和类绑定来通过此测试:

<div
v-for="todo in todos"
:key="todo.id"
data-test="todo"
:class="[todo.completed ? 'completed' : '']"
>
{{ todo.text }}
<input type="checkbox" v-model="todo.completed" data-test="todo-checkbox" />
</div>

恭喜!您编写了第一个组件测试。