Vitest 测试实践
Vitest 测试实践
Breezli为什么选择Vitest
Jest 与 Vitest对比
Jest | Vitest | |
---|---|---|
构建基础 | Webpack / Babel | 原生 ESM,基于 Vite |
启动速度 | 较慢(需打包,但提供了缓存机制) | 极快(无需打包,Vite 的快速冷启动,按需加载) |
内存占用 | 高 | 低 |
语法支持 | 需要额外的配置来支持现代 JavaScript(例如 ES Modules、TypeScript)需要安装额外的转换器(如 Babel 或 ts-jest) | 不需要额外适配 |
调试体验 | 一般 | Watch和热更新,可直接在浏览器/VS Code 中调试 |
社区生态 | 成熟,插件丰富 | 快速发展,Vue/React 生态支持良好 |
所以,对于使用 Vite 的项目,Vitest 是更现代、更高效的选择
⚠️ 但 Jest 仍适用于 Node.js 服务端或复杂兼容性场景
使用
核心测试工具
exprect
expect
断言:验证行为是否符合预期
1 | // 检查字符串是否包含子串 |
更多方法
匹配器 | 功能描述 |
---|---|
toBe(value) | 检查值是否严格等于(===)指定的值,通常用于原始类型(如数字、字符串)。 |
toEqual(value) | 检查对象或数组的内容是否深度相等。 |
toContain(item) | 检查数组或可迭代对象是否包含指定的元素。 |
toContainEqual(item) | 检查数组或可迭代对象是否包含与指定值深度相等的元素。 |
toBeTruthy() | 检查值是否为“真值”(truthy),即在布尔上下文中为 true。 |
toBeFalsy() | 检查值是否为“假值”(falsy),即在布尔上下文中为 false。 |
toBeNull() | 检查值是否为 null。 |
toBeUndefined() | 检查值是否为 undefined。 |
toBeDefined() | 检查值是否不是 undefined。 |
toBeNaN() | 检查值是否为 NaN。 |
toBeGreaterThan(number) | 检查值是否大于指定的数字。 |
toBeGreaterThanOrEqual(number) | 检查值是否大于或等于指定的数字。 |
toBeLessThan(number) | 检查值是否小于指定的数字。 |
toBeLessThanOrEqual(number) | 检查值是否小于或等于指定的数字。 |
toHaveLength(number) | 检查数组、字符串或其他可迭代对象的长度是否等于指定值。 |
toThrow(error?) | 检查函数是否抛出错误,可以指定错误消息或错误类型。 |
toMatch(regexpOrString) | 检查字符串是否匹配指定的正则表达式或子字符串。 |
toMatchObject(object) | 检查对象是否部分匹配指定的对象结构。 |
toHaveProperty(keyPath, value?) | 检查对象是否具有指定的属性,可以进一步检查属性值。 |
toBeInstanceOf(Class) | 检查对象是否是某个类的实例。 |
toBeCloseTo(number, numDigits?) | 检查浮点数是否接近指定值,避免精度问题。 |
toHaveBeenCalled() | 检查模拟函数是否被调用过。 |
toHaveBeenCalledTimes(number) | 检查模拟函数是否被调用了指定次数。 |
toHaveBeenCalledWith(…args) | 检查模拟函数是否被调用时传入了指定的参数。 |
toHaveBeenLastCalledWith(…args) | 检查模拟函数最后一次调用时是否传入了指定的参数。 |
toHaveBeenNthCalledWith(n, …args) | 检查模拟函数第 n 次调用时是否传入了指定的参数。 |
toHaveReturned() | 检查模拟函数是否成功返回(没有抛出错误)。 |
toHaveReturnedTimes(number) | 检查模拟函数是否成功返回了指定次数。 |
toHaveReturnedWith(value) | 检查模拟函数是否返回了指定的值。 |
toHaveLastReturnedWith(value) | 检查模拟函数最后一次调用是否返回了指定的值。 |
toHaveNthReturnedWith(n, value) | 检查模拟函数第 n 次调用是否返回了指定的值。 |
toBeCalled() | 等同于 toHaveBeenCalled(),检查模拟函数是否被调用过。 |
toBeCalledTimes(number) | 等同于 toHaveBeenCalledTimes(number),检查模拟函数是否被调用了指定次数。 |
toBeCalledWith(…args) | 等同于 toHaveBeenCalledWith(…args),检查模拟函数是否被调用时传入了指定参数。 |
lastCalledWith(…args) | 等同于 toHaveBeenLastCalledWith(…args),检查模拟函数最后一次调用时是否传入了指定参数。 |
mount
mount
:完整挂载组件进行测试
❗
mount
不返回 JSX 虚拟节点,而是返回一个 Wrapper 对象,用于操作和断言组件行为。
1 | import { mount } from '@vue/test-utils' |
wrapper
提供以下常用方法:
wrapper.text()
:获取渲染文本wrapper.classes()
:获取类名数组wrapper.find()
:查找 DOM 元素wrapper.trigger()
:触发事件wrapper.emitted()
:检查事件是否被触发
推荐使用
mount
而非shallowMount
,以测试真实渲染结果和子组件交互。
编写高质量测试用例
与ai结合
先使用AI编写一套 提问模板
包括角色身份定位(产品经理)、需求、背景、输出规范等
比如我在开发组件库的按钮👇
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36 # 身份定位
- **角色**:互联网产品经理
- **目标**:产品需求分析和功能点设计
# 需求
以"[XXX]"形式定义变量用于对话中不同任务的触发指令
以"/help" 为触发关键词,列出所有定义的变量`**XXX**`以及代表的任务
对话过程用中文交流,专业术语可用英文或缩写。
- [XQFX]:(需求分析) 根据给出的内容输出需求分析文档(md)
- [GNSJ]:(功能设计) 以上文中的 "需求分析文档" 为依据
# 背景
(项目文档地址:https://xxx/button.com )
首次可补充提问来完善背景
# 输出规范
- **需求分析**[XQFX]
- **格式**:用户调研摘要、竞品对比报告、市场趋势分析。
- **内容**:用户痛点、期望功能、安全性需求。
- **功能点设计**[GNSJ]
- **格式**:功能描述、api 设计、交互关系。
- **内容**:功能实现细节、用户操作流程、异常处理。
# 示例指令
- **需求分析**:[XQFX]组件库按钮组件。
- **功能点设计**:[GNSJ]
请在后续对话中使用上述结构和示例指令来指导任务执行。
接着 [XQFX]
、[GNSJ]
,AI会返回给你 需求分析与功能设计文档
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 好的,以下是“Button | Leatom”组件的需求分析和功能设计文档:
---
# 需求分析文档:Button | Leatom 组件
## 一、用户调研摘要
### 用户痛点
......
# 功能点设计文档:ButtonPlus | Veyra 组件
## 一、功能描述
### 1.1 功能目标
设计一个具有丰富视觉特效和高度可定制性的按钮组件,以满足不同用户的需求,同时提升开发效率。
### 1.2 功能范围
......
## 三、交互关系
......
---
以上是“Button | Leatom”组件的需求分析和功能设计文档。如果需要进一步调整或补充,请随时告知。
再把AI生成的 需求分析文档 再以丢给新的对话
你是一个优秀的测开工程师,现在需要你根据我给你的需求分析帮我写测试用例
要求:vitest 技术栈,mount 尽量返回 JSX 虚拟节点的形式(更贴近实际的渲染结果,代码也更加简洁直观),it,test 等方法的 name 属性用中文描述(英文更规范,但是我英语差),以下是需求文档内容
结果返回就是测试用例了
1
2
3
4
5
6 test('renders button with correct text', () => {
......
})
test('......', () => {
......
})
测试覆盖率
覆盖率类型说明
类型 | 说明 |
---|---|
行覆盖率(Line Coverage) | 每一行代码是否被执行 |
语句覆盖率(Statement Coverage) | 每个独立语句是否执行 |
分支覆盖率(Branch Coverage) | 每个 if/else、三元运算符等分支是否覆盖 |
函数覆盖率(Function Coverage) | 每个函数是否至少调用一次 |
Vitest 使用
istanbul
报告,可通过--coverage
生成 HTML 报告
合理设定目标
项目类型 | 推荐目标 |
---|---|
安全关键模块(支付、权限) | ≥ 90% 分支覆盖率 |
核心业务组件 | ≥ 80% 行覆盖率 |
普通 UI 组件 | ≥ 70% 行覆盖率 |
避免“为了覆盖而覆盖”,无意义的测试会降低维护成本
保障高质量测试的策略
分层测试策略
层级 | 目标 | 工具 |
---|---|---|
单元测试 | 验证单个组件/函数 | Vitest |
集成测试 | 模块间协作 | Vitest + Mock |
E2E 测试 | 用户完整流程 | Cypress / Playwright |
回归测试 | 修改后不引入新 bug | CI 自动运行 |
持续集成(CI)自动化
1 | # .github/workflows/test.yml |
每次 PR 自动运行测试,覆盖率下降时告警