公众号:小博的前端笔记
1、已有项目集成 vitest
-
第一步安装依赖
npm install -D vitest
官方提示Vitest 需要 Vite >=v5.0.0 和 Node >=v18.0.0, 但是我们项目vite版本为2.9.9,也能安装成功。

- 第二步创建文件夹,暂时将其放在src/unitTest目录下

- 第三步增加执行命令
json
{
"scripts": {
"test:unit": "vitest"
}
}

- 第四步进行运行测试,不需要额外配置,vitetest和vite集成较好且会自动查找项目中*文件名中包含 ".test." 或 ".spec." 。*的文件

并且此时对测试文件进行更改,热更新还是生效的!
如果想在测试期间想要不同的配置,可以:创建
vitest.config.ts
文件优先级会最高.
对比jest
若我们使用jest,则需要进行以下步骤才能使用:
安装依赖--->配置jest.config文件-->配置babel--->package.json增加执行命令--->运行测试
不确定是否有热更新
反观vitejest:
安装依赖--->package.json增加执行命令--->运行测试
只需要简单3步就能运行测试了,若无特殊要求不需要创建vitest.config.ts
文件。
2、实际功能初步使用
2.1 普通ts/js文件方法测试
以甘特图为例,甘特图中存在嵌套表格,此时的数据结构为树形结构 ,我们将树形结构扁平化进行使用,这样可以避免使用递归组件。
我们对扁平化方法进行单元测试:
- 再测试文件中引入甘特图的扁平化方法

-
因为表格会有展开、收缩的功能,所以此扁平化方法也需根据需求进行对应层级的扁平化化
-
1、mock一个树形结构数据
-
2、和想要的结果进行对比
-


markdown
其中第一个测试方法得到的结果应该是正确的,第二个方法应该是错误的来看运行结果:

markdown
标出了测试运行的结果和对比的结果。
我们改为预期的结果再运行即可测试通过!

2.2 vue文件测试环境配置
我们项目都为vue框架项目,所以很多需要测试的方法都是再.vue
文件中的,所以需要支持对vue文件的单元测试。
vitest是测试框架
本身不支持对vue框架的语法测试,所以我们需要一个能对vue进行测试的程序库Vue Test Utils
,此库由官方提供,是一组实用函数,旨在简化 Vue.js 组件的测试。它提供了一些以独立方式挂载和与 Vue 组件交互的方法。
可以将其与任何测试运行器一起使用.
官方地址:test-utils.vuejs.org/zh/guide/
安装
css
npm install --save-dev @vue/test-utils
运行demo
出现报错:properlyReferenceError: document is not defined

1. 问题原因
- 测试框架(如 Jest/Vitest)默认在 Node.js 环境运行,没有浏览器 DOM(如
document
对象) - 当组件或测试代码直接操作 DOM 时,Node.js 环境会报错
2. 解决方案
-
下载
jsdom
依赖包并选择次环境cssnpm install --save-dev jsdom
-
增加
vitestconfigts
配置文件,并根据项目已有viteconfigts
文件进行合并改动,如下 -
改完后成功测试vue组件
2.3 官方ToDoApp得到的注意事项
test-utils.vuejs.org/zh/guide/es...
mount说明事项:
- 这是在 VTU 中渲染组件的主要方式。
- 调用
mount
并将组件 作为第一个参数传入------这是几乎每个测试都会执行的操作。 - 将结果赋值给一个名为
wrapper
的变量,因为mount
提供了一个简单的"包装器",它为测试提供了一些方便的方法。
同步异步事项:
- Jest 以同步方式执行测试,且在最后一个函数调用后立即结束测试。
- Vue 会异步更新 DOM。所以我们需要将测试标记为
async
,并在任何可能导致 DOM 变化的方法上调用await
。
测试阶段事项:
-
测试分为三个不同的阶段:布置 (Arrange) 、执行 (Act) 和断言 (Assert)
- 在布置 (Arrange) 阶段,我们为测试设置场景。更复杂的示例可能需要创建 Vuex store 或填充数据库。
- 在执行 (Act) 阶段,我们模拟用户如何与组件或应用程序交互。
- 在断言 (Assert) 阶段,我们对组件的当前状态进行断言。
几乎所有测试都将遵循这三个阶段。
例如:

2.4 wrapper的部分API介绍
API | 作用 | 本质 | 备注 |
---|---|---|---|
get() | 用于查找存在的元素 | 使用 querySelector 语法 |
没有找到匹配选择器的元素,它会抛出错误,测试将会失败。如果找到了元素,get() 返回一个 DOMWrapper 。 |
find()和 exists() | 用于查找元素 | get() 基于元素存在的假设来工作,当元素不存在时会抛出错误。因此,不推荐使用它来断言元素是否存在。为此,我们使用 find() 和 exists() 。 |
|
data | 挂载选项覆盖默认值 | 挂载选项 | 挂载选项中的 data 会优先于任何默认值。 |
isVisible() | 检查隐藏元素的能力 | isVisible() 会检查: 1、元素或其祖先元素是否具有display: none 、visibility: hidden 或opacity: 0 样式; 2、元素或其祖先是否位于折叠的<details> 标签内; 3、元素本身或其祖先元素是否有hidden attribute; 在以上任一情况下,isVisible() 都会返回 false。 |
使用data时需注意组合式写法需以下写法(官方给的是声明式写法):

总结
- 使用
find()
结合exists()
验证元素是否在于 DOM 中。 - 如果你确认元素存在于 DOM 中,就使用
get()
。 - 可以使用
data
挂载选项设置组件的默认值。 - 使用
get()
和isVisible()
验证在 DOM 中元素的可见性。
3、编写易于测试的组件
3.1、不要测试实现细节
从用户的角度考虑输入和输出。以下大致是在为 Vue 组件编写测试时应当考虑的所有内容:
输入 | 示例 |
---|---|
交互 | 点击、输入......及任何"人类"交互 |
Props | 组件接收的参数 |
数据流 | 从 API 调用、数据订阅等中传入的数据 |
输出 | 示例 |
---|---|
DOM 元素 | 渲染到文档中的任何可观察节点 |
事件 | 触发的事件(使用 $emit ) |
副作用 | 如 console.log 或 API 调用 |
其他一切都是实现细节。
注意,这个列表不包含内部方法、中间状态或甚至数据等元素。
经验法则是测试在重构时不应失败 ,也就是说,当我们更改其内部实现而不改变其行为 时,测试不应失败。如果发生这种情况,则测试可能依赖于实现细节。
例如,假设有一个简单的计数器组件,包含一个增加计数的按钮:
xml
<!-- Counter.vue -->
<script setup>
import { ref } from 'vue'
const count = ref(0)
const increment = () => {
count.value++
}
</script>
<template>
<p class="paragraph">Times clicked: {{ count }}</p>
<button @click="increment">Increment</button>
</template>
我们可以编写以下测试:
javascript
import { mount } from '@vue/test-utils'
import Counter from './Counter.vue'
test('counter text updates', async () => {
const wrapper = mount(Counter)
const paragraph = wrapper.find('.paragraph')
expect(paragraph.text()).toBe('Times clicked: 0')
await wrapper.setData({ count: 2 })
expect(paragraph.text()).toBe('Times clicked: 2')
})
注意这里我们在更新其内部数据,并且也依赖于用户视角的细节。
注意,更改数据或 CSS 类名都会导致测试失败。然而,组件仍然可以按预期工作。这种情况称为误报 (false positive) 。
相反,以下测试尽量使用前文提到的输入和输出:
javascript
import { mount } from '@vue/test-utils'
test('text updates on clicking', async () => {
const wrapper = mount(Counter)
expect(wrapper.text()).toContain('Times clicked: 0')
const button = wrapper.find('button')
await button.trigger('click')
await button.trigger('click')
expect(wrapper.text()).toContain('Times clicked: 2')
})
像 Vue Testing Library 这样的库就是基于这些原则构建的。
3.2、构建更小、更简单的组件
有时,一个组件可能包含复杂的方法、密集的计算或使用多个依赖项。
这里的建议是提取该方法并将其导入组件。这样,你可以使用 Jest 或其他测试运行器独立测试该方法。
此外,如果这个复杂方法难以调试或速度较慢,你可能希望对其进行模拟,以使测试更简单和更快速。发起 HTTP 请求中的示例就是一个很好的例子------axios 是一个很复杂的库!
3.3、在编写组件之前编写测试
如果你提前编写测试,就不会写出不可测试的代码!
4、 实际vue文件功能测试
4.1、测试计划管理快速筛选功能(http模拟数据输入+dom显示)
-
测试目的:
- 快速搜索组件显示的人员是否正确
-
测试思路:
- 我们的第一个目标是测试这个组件而不实际访问 API。这样会导致测试变得不稳定且可能执行缓慢。
- 其次,我们需要断言组件是否以正确的参数进行了正确的调用。虽然我们不会从 API 获取结果,但仍需确保请求了正确的资源。
- 此外,我们还需要确保 DOM 已相应更新并显示数据。我们可以使用
@vue/test-utils
中的flushPromises()
函数来实现。
-
具体实现:
运行后报错如下:

虽然此文件是在vite.config中引入的,但是此文件是全局的所以测试文件也会引入,但是此时的路径会相对于测试文件,所以我们将路径改为绝对路径
继续运行报错如下:

经各种查询得知被测试的组件需要挂载所需的三方库,比如element、pinia、i18n等等,所以进行以下处理


继续运行,报错如下:

原因没有相关缓存。。。。,其实此时我们可以对store做单独的处理*(mock store)*使其处于一个干净的环境,此处先不处理,直接对语法进行兼容。
继续运行,报错如下:

原因: ResizeObserver
是浏览器环境中的 API,而 Vitest 默认在 Node.js 环境中运行测试时无法识别它,虽然我们配置了jsdom环境,但是可能是jsdom也没有包含ResizeObserver ,、
解决:1、模拟ResizeObserver ;2、下载三方库, 我们选择1
新建setup.ts文件,模拟ResizeObserver ,如下图:

继续运行后报错:

原因: 经过排查,
此代码未生效,因为vue-utils给的是jest的实例,毕竟vitest和jest还是有区别的,所以重新整理我们目的是拦截http请求并返回模拟数据,所以经过多次询问D哥,了解到两个模拟库:
MSW
#### **选择 MSW 的场景**
* **需要全面拦截网络请求** :例如项目中同时使用 `axios`、`fetch` 或其他 HTTP 客户端。
* **集成测试或端到端测试**:希望模拟真实网络环境,验证请求与响应的完整流程。
* **无侵入性需求**:不想在业务代码中引入任何测试逻辑,保持代码纯净。
* **复杂场景模拟** :如模拟请求延迟(`delay`)、动态响应生成、错误重试等。
* **示例代码(MSW 配置)** :
*
```javascript
import { setupWorker, rest } from 'msw'
const worker = setupWorker(
rest.get('/api/data', (req, res, ctx) => {
return res(ctx.json({ data: 'mocked' }))
})
)
worker.start()
```
axios-mock-adapter
#### **选择 axios-mock-adapter 的场景**
- 仅需模拟 axios 请求:项目完全依赖 axios,无需处理其他 HTTP 客户端。
- 单元测试轻量化:快速为 axios 实例配置 mock 响应,适合单一功能模块测试。
- 简单错误模拟:例如直接返回 500 错误或特定响应状态码。
- 示例代码(axios-mock-adapter 配置) :
```javascript
import axios from 'axios'
import MockAdapter from 'axios-mock-adapter'
const mock = new MockAdapter(axios)
mock.onGet('/api/data').reply(200, { data: 'mocked' })
```
我们此时只需要模拟axios即可,不需要跨平台,所以选择axios-mock-adapte
css
npm install axios-mock-adapter --save-dev
修改后的代码如下:


运行单元测试发现报错如下:

原因:因为我们改为了类名进行测试,拥有此类型的第一个下标为全部按钮,修改过后成功通过测试!

4.2、总结:
-
1、组件中的http采用axios-mock-adapter进行模拟即可。
-
2、组件中使用到的三方库需进行挂载,比如element、pinia、i18n等;
- 对三方库进行全局挂载,在创建的setup配置文件中

