单元测试实战:从零搭建到完整落地
本文是 单元测试的实战指南,面向希望将单元测试真正落地到前端项目的开发者。全文采用步骤化、可复现的操作方式,确保你能够快速掌握 的核心用法,并建立可靠的测试体系。
一、核心结论:为什么选择 以及本文能帮你达成什么
如果你正在寻找一个 与 Vite 生态无缝集成、启动速度快、配置简单、且具备 Jest 类似 API 的单元测试框架, 是当前的最优解。通过本文,你将完整实现以下目标:
1. 在现有 Vite 项目(或支持 ESM 的项目)中 5 分钟内完成 的安装与基础配置。
2. 掌握单元测试的核心编写规范,包括组件测试、工具函数测试、异步测试、Mock 模块。
3. 配置测试覆盖率报告,确保核心模块测试充分。
4. 将 集成到 CI/CD 流程中,形成自动化质量门禁。
本文所有操作均基于 官方最新稳定版(v3.x,当前 2026 年推荐版本),所有代码示例均可直接复制运行。
二、前置环境与安装(5 分钟完成)
在开始之前,请确保你的环境满足以下条件:
Node.js:版本 18.x 或更高(推荐 20.x LTS)
包管理器:npm / yarn / pnpm 均可
项目基础:已存在一个使用 Vite 构建的项目(如 Vue / React / 项目),或任何支持 ESM 的 Node.js 项目。
2.1 安装 及相关依赖
在项目根目录执行以下命令:
# 使用 npm
npm -D @/ui @/-v8
# 使用 yarn
yarn add -D @/ui @/-v8
# 使用 pnpm
pnpm add -D @/ui @/-v8
依赖说明:
:核心测试框架。
@/ui:可视化 UI 界面,方便查看测试运行状态。
@/-v8:基于 V8 的代码覆盖率工具,用于生成测试覆盖率报告。
2.2 在 .json 中添加测试脚本
{
"": {
"test": "",
"test:ui": " --ui",
"test:": " --"
}
}
2.3 创建 配置文件(可选,但有强烈推荐)
在项目根目录创建 ..ts 文件。如果你的项目已经有 vite..ts, 会自动继承其配置,但通常我们需要为测试环境单独调整。
{ } from '/';
vue from '@/-vue'; // 如果是 Vue 项目
react from '@/-react'; // 如果是 React 项目
({
: [vue()], // 或 react()
test: {
// 环境配置:jsdom 用于模拟浏览器环境,node 用于 Node.js 环境
: 'jsdom',
// 全局 API,如 、it、 可直接使用,无需
: true,
// 测试文件匹配模式
: ['/.{test,spec}.{js,ts,jsx,tsx}'],
// 覆盖率配置
: {
: 'v8',
: ['text', 'json', 'html'],
: ['/', 'dist/', '..ts'],
},
},
});
注意:若使用 : true,需在 .json 中添加 类型定义:
{
"": {
"types": ["/"]
}
}
三、编写第一个测试(实战入门)
3.1 测试一个纯函数
假设我们有一个工具函数 add,位于 src/utils/math.ts:
add(a: , b: ): {
a + b;
}
创建测试文件 src/utils/math.test.ts:
{ , it, } from '';
{ add } from './math';
('math 工具函数测试', () => {
it('add 函数应正确计算两个数字的和', () => {
(add(1, 2)).toBe(3);
(add(-1, 1)).toBe(0);
(add(0.1, 0.2)).(0.3); // 浮点数比较用
});
});
运行测试:
npm run test
你将看到测试通过的输出。
3.2 测试 Vue 组件(以 Vue 3 + API 为例)
安装测试辅助库:
npm -D @vue/test-utils @/-vue jsdom
测试一个简单的计数器组件 .vue:
<>
<div>
<p>Count: {{ count }}</p>
< @click=""></>
</div>
</>
< setup lang="ts">
{ ref } from 'vue';
const count = ref(0);
const = () => count.value++;
</>
创建测试文件 src//.test.ts:
{ , it, } from '';
{ mount } from '@vue/test-utils';
from './.vue';
(' 组件', () => {
it('点击按钮后计数应增加', async () => {
const = mount();
// 初始渲染值为 0
(.text()).('Count: 0');
// 查找按钮并点击
const = .find('');
await .('click');
// 点击后值为 1
(.text()).('Count: 1');
});
});
关键点:mount 来自 @vue/test-utils, 用于模拟用户事件,由于 Vue 的响应式更新是异步的,需要使用 await。
3.3 测试 React 组件
安装测试辅助库:
npm -D @-/react @-/jest-dom jsdom
测试一个简单的计数器组件 .tsx:
{ } from 'react';
() {
const [count, ] = (0);
(
<div>
<p>Count: {count}</p>
< ={() => (count + 1)}></>
</div>
);
}
创建测试文件 src//.test.tsx:
{ , it, } from '';
{ , } from '@-/react';
from '@-/user-event';
from './';
(' 组件', () => {
it('点击按钮后计数应增加', async () => {
(< />);
// 获取元素
const = .('');
const = .(/Count: /);
().('Count: 0');
// 模拟点击
await .click();
().('Count: 1');
});
});
四、核心测试能力详解
4.1 匹配器()
内置了丰富的匹配器,兼容 Jest 的 API。
| 匹配器 | 用途 | 示例 |
|---|---|---|
toBe(value) |
基本值相等(使用 .is) |
(1+1).toBe(2) |
(value) |
深度相等(对象/数组) | ({a:1}).({a:1}) |
() |
值为真 | (true).() |
() |
值为假 | (false).() |
(item) |
数组/字符串包含元素 | ([1,2]).(2) |
(error?) |
函数抛出错误 | (() => fn()).() |
(num) |
浮点数近似相等 | (0.1+0.2).(0.3) |
4.2 异步测试
对于返回 的异步函数,可以使用 async/await 或 /。
示例:异步函数
async () {
{ id: 1 };
}
it(' 应返回正确数据', async () => {
const data = await ();
(data).({ id: 1 });
});
// 或者使用
it(' 应返回正确数据(方式)', () => {
(())..({ id: 1 });
});
4.3 Mock(模拟)
提供了强大的 Mock 功能,用于隔离依赖。
Mock 函数:
{ vi } from '';
const = vi.fn();
(1, 2);
().(1, 2);
Mock 模块:
假设有一个 api.ts 模块:
const = async (id: ) => {
// 真实 HTTP 请求
};
在测试中 Mock 它:
{ vi, , it, } from '';
{ } from './api';
vi.mock('./api', () => ({
: vi.fn(),
}));
it('测试使用 的函数', async () => {
( as any).({ name: 'John' });
// 执行被测试函数...
});
Mock 全局对象:
// Mock .
.(, '', {
value: { href: '; },
: true,
});
4.4 生命周期钩子
| 钩子 | 作用 |
|---|---|
|
所有测试前执行一次 |
|
所有测试后执行一次 |
|
每个测试前执行 |
|
每个测试后执行 |
示例:
{ , , , it, } from '';
('数据库操作测试', () => {
(() => {
// 初始化测试数据
});
(() => {
// 清理测试数据
});
it('应该成功插入数据', () => {
// 测试代码
});
});
五、代码覆盖率配置与解读
5.1 运行覆盖率测试
npm run test:
运行后,终端会输出覆盖率摘要,同时在项目根目录生成 文件夹,内含 HTML 报告(/index.html)。
5.2 覆盖率指标解读
% Stmts(语句覆盖率):代码中至少执行过一次的语句比例。
% (分支覆盖率):控制结构(if、等)中每个分支被执行的比例。
% Funcs(函数覆盖率):被调用过的函数比例。
% Lines(行覆盖率):被覆盖的代码行比例。
5.3 设置覆盖率门槛
在 ..ts 中可配置最低阈值,当覆盖率不达标时测试失败:
({
test: {
: {
: {
: 80,
: 80,
: 80,
lines: 80,
},
},
},
});
六、集成到 CI/CD 流程
6.1 示例
创建 .//test.yml:
name: Run Tests
on:
push:
: [main, ]
:
: [main]
jobs:
test:
runs-on: -
steps:
uses: /@v4
uses: /setup-node@v4
with:
node-: '20'
cache: 'npm'
run: npm ci
run: npm run test:
name:
uses: /-@v4
with:
token: ${{ . }}
6.2 本地 Git Hooks(使用 Husky + lint-)
在提交前自动运行相关测试:
npx husky add .husky/pre- "npx lint-"
在 .json 中配置:
{
"lint-": {
".{js,ts,vue,jsx,tsx}": [
" --run"
]
}
}
七、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
find 'vue' |
测试环境缺少 Vue 的依赖 | 确保已安装 vue 并在 ..ts 中使用 : [vue()] |
is not |
测试环境未设置为 jsdom |
在配置中设置 : 'jsdom' |
: use a |
Node.js 默认不支持 ESM | 在 .json 中设置 "type": "",或使用 ---vm- 标志 |
| 测试运行很慢 | 默认开启单进程 | 可使用 -- 或 --pool=forks 调整并发策略 |
八、总结与进阶建议
通过本文,你已经完成了 单元测试从安装、配置、编写到 CI 集成的完整流程。为了进一步提升测试质量,建议:
1. 遵循测试金字塔:多写单元测试(占比最高),少写端到端测试。
2. 测试命名规范:使用 描述模块,it 描述具体行为,形成可读的测试文档。
3. 保持测试独立性:每个测试不依赖其他测试的执行顺序。
4. 定期审查覆盖率报告:关注未覆盖的代码行,及时补充测试。

