Vitest 入门:下一代单元测试框架
Jest 统治了 JS 测试很多年,Vitest 出现后新项目几乎都换了——原因就一个字:快。以及它和 Vite 配置的无缝共享。
Vitest 是什么
Vitest 是跑在 Vite 之上的单元测试框架,API 与 Jest 高度兼容。它解决 Jest 的两大痛点:
慢。Jest 自带一套模块转换管道,冷启动慢、文件多时 watch 模式内存膨胀。Vitest 直接复用 Vite 的转换层和 esbuild——你的项目 Vite 能跑多快,测试就能跑多快。
配置割裂。Jest 项目要单独维护一套 jest.config.js(别名、插件、转换规则,和构建配置重复)。Vitest 直接读 vite.config.ts:路径别名、环境变量、插件,构建时怎么配测试时就怎么配,一处维护。
基本用法
pnpm add -D vitest
// utils/date.test.ts
import { describe, it, expect } from 'vitest';
import { formatDate, isExpired } from './date';
describe('formatDate', () => {
it('把 Date 格式化为 YYYY-MM-DD', () => {
expect(formatDate(new Date('2026-09-25'))).toBe('2026-09-25');
});
it('null 输入返回空串', () => {
expect(formatDate(null)).toBe('');
});
});
describe('isExpired', () => {
it('过期时间为过去返回 true', () => {
expect(isExpired(new Date('2000-01-01'))).toBe(true);
});
});
// package.json
{ "scripts": { "test": "vitest run" } } // CI 用 run(单次),本地开发用 vitest(watch)
API 心智与 Jest 完全一致:describe 分组、it 用例、expect 断言。从 Jest 迁移基本是把 import 来源换掉,配一个兼容别名甚至源码都不用动:
// vitest.config.ts —— defineConfig 从 vite 导入,配置全复用
import { defineConfig } from 'vite';
export default defineConfig({
test: { environment: 'node', globals: true },
});
核心能力速览
异步与时间控制(测试定时逻辑的神器):
import { vi } from 'vitest';
it('30 秒后标记过期', () => {
vi.useFakeTimers(); // 假时钟:不用真等 30 秒
const session = createSession();
vi.advanceTimersByTime(31_000); // 时间快进
expect(session.expired).toBe(true);
vi.useRealTimers();
});
Mock 模块与函数:
// mock 整个模块——测试「调用方逻辑」而不真调外部接口
vi.mock('@/lib/api', () => ({
fetchArticle: vi.fn().mockResolvedValue({ id: 1, title: 'mock 文章' }),
}));
// mock 返回值/次数
const spy = vi.fn();
spy.mockReturnValueOnce(42);
expect(spy).toHaveBeenCalledTimes(1);
DOM 测试:配 environment: 'jsdom' 可测组件逻辑;组件渲染级测试配 Vue Test Utils 使用。
覆盖率:vitest run --coverage 一条命令,底层 v8 引擎取数据,速度远快于 istanbul。
测试金字塔在前端怎么落
务实的分层建议:
必须测:纯函数(格式化、校验、状态计算)、工具库、store 逻辑——输入输出明确,测试性价比最高。本站前端的测试集中在这层(如 knowledge-preview 的 URL 解析、学习模块的工具函数)。
值得测:复杂交互逻辑(表单联动、状态机流转),用 jsdom + 组件库驱动。
不值得强求:纯展示组件(模板对不对肉眼一看便知)、端到端流程——那是 Playwright 的领地,且维护成本高。
一个反直觉但重要的原则:测试保护的是「重构自由」。有了测试网,改代码才有底气;为覆盖率数字写的「断言 undefined 不报错」式测试是负资产——测试也要被 review,烂测试比没测试更糟。
watch 模式:TDD 的体验
本地开发 pnpm vitest 进 watch 模式:只重跑受改动影响的测试文件(Vite 的模块图让它能精确知道谁依赖谁)。保存 → 相关测试毫秒级回跑 → 红绿反馈。这个反馈循环快到让你愿意写测试——测试写不写的真实原因往往不是理念,是反馈太慢。
小结
Vitest 的选型逻辑:用 Vite 的项目直接选它(配置零成本、速度碾压);新项目没有历史包袱也选它。API 兼容 Jest 意味着学习成本趋近于零。写测试记住价值排序:纯函数 > 复杂逻辑 > 展示组件,测试的目标是重构自由,不是覆盖率数字。