起因
JS 项目测试框架历史:
- mocha + chai + sinon(老)
- jest(Facebook,2014+,事实标准)
- vitest(Vite 团队,2021+,Vite-native)
新项目选谁?老 jest 项目要不要迁?下面经验。
jest
// my.test.js
const { add } = require('./math');
describe('math', () => {
test('add', () => {
expect(add(1, 2)).toBe(3);
});
});
npx jest
10 年生态,全 JS 圈子默认。
优势
- 教程 / 答案最多
- 巨大插件生态(jest-dom / jest-axe / ...)
- snapshot testing 成熟
- mock 系统强(auto-mock / manual mock)
劣势
- 启动慢(2-5 秒只为跑 1 个测试)
- ESM 支持差(CJS 优先,ESM 配置复杂)
- TS 要 ts-jest / @swc/jest 等
- 慢(百 test 几十秒)
vitest
// my.test.js
import { test, expect, describe } from 'vitest';
import { add } from './math';
describe('math', () => {
test('add', () => {
expect(add(1, 2)).toBe(3);
});
});
npx vitest
API jest-compat(expect / describe 大部分一致)。
基于 Vite,原生 ESM + esbuild + TS 直接。
优势
- 极快:watch mode HMR-like,改文件即重跑相关 test
- ESM 原生 + TS 原生
- Vite 项目同 config 共享
- jest API 兼容(容易迁移)
- 内置 coverage / UI / browser mode
劣势
- 生态比 jest 小(但每年增长快)
- 某些 jest 插件没等价
- snapshot 跟 jest 略不同(导致迁移微调)
性能对比
中型项目 500 test:
| jest | vitest | |
|---|---|---|
| cold start | 8s | 1.5s |
| 全跑 | 25s | 6s |
| watch(改 1 file) | 5s | 0.3s |
| coverage | 35s | 10s |
vitest 普遍 3-5x 快。开发循环 watch mode 差距更大。
写法对比
写法 99% 一致:
// 通用
describe('x', () => {
beforeEach(() => { ... });
test('does y', async () => {
expect(...).toBe(...);
});
});
vitest 加:
import { vi } from 'vitest'; // jest 是全局 vi → jest.fn / mock
jest.fn() → vi.fn(),jest.spyOn → vi.spyOn,jest.mock → vi.mock。
config 注入全局可让 jest 写法直接跑:
// vitest.config.ts
export default defineConfig({
test: { globals: true }, // 启用 describe / test / expect 全局
});
mock
// vitest
import { vi } from 'vitest';
vi.mock('./api', () => ({
fetchUser: vi.fn(() => Promise.resolve({ name: 'mock' })),
}));
const spy = vi.spyOn(console, 'log');
跟 jest 几乎一样。
snapshot
expect(rendered).toMatchSnapshot();
生成 __snapshots__/my.test.js.snap。
vitest 用 jest 相同格式。
inline snapshot:
expect(rendered).toMatchInlineSnapshot(`"<div>hello</div>"`);
review 时 inline 直观。
React Testing Library 集成
// vitest.config.ts
import { defineConfig } from 'vitest/config';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
test: {
environment: 'jsdom',
setupFiles: './test/setup.ts',
},
});
// test/setup.ts
import '@testing-library/jest-dom/vitest'; // matchers
import { render, screen } from '@testing-library/react';
import { test, expect } from 'vitest';
import App from './App';
test('renders title', () => {
render(<App />);
expect(screen.getByText('Hello')).toBeInTheDocument();
});
跟 jest 配 RTL 几乎一样。
browser mode(vitest 1.0+)
test: {
browser: { enabled: true, name: 'chromium' },
}
测试在真实浏览器跑(替代 jsdom)。
适合:测 component 在真实环境(layout / CSS / fetch)。
jest 不能直接跑浏览器(要 jest-playwright 等组合)。
迁移 jest → vitest
npm install -D vitest @vitest/ui
package.json:
"scripts": {
"test": "vitest",
"test:ui": "vitest --ui",
"test:coverage": "vitest --coverage"
}
代码层 99% 测试直接跑:
// 全局 import 替代
- import { describe, test, expect } from '@jest/globals';
+ // (with globals: true 不需要)
+ import { describe, test, expect } from 'vitest';
- jest.fn()
+ vi.fn()
- jest.mock('./api')
+ vi.mock('./api')
一般 sed 批量替换 + 个别手动调。
中型项目几小时迁移完。
CI 集成
- run: pnpm test --coverage
- uses: codecov/codecov-action@v4
不变。GitHub Actions / GitLab / CircleCI 都 vitest 友好。
真实迁移 case
我们一个 React 项目,jest + RTL + 200 test:
- ci 时间从 90s → 25s
- watch mode 改文件秒返馈
- ts-jest 配置删了(vite 自带 TS)
- 配置文件简化
但迁完一周才完全稳:
- 几个 mock 行为微差
- snapshot whitespace 略不同需 regenerate
- 某些 jest plugin(如 jest-axe)没 vitest 版本(找替代)
何时不必迁
- 老项目大 jest,团队稳定 → 不动
- 用极偏 jest 插件 → 维持
决策
- 新项目 → vitest(无脑选)
- 大老项目 + 团队稳 → jest,可不迁
- 小老项目 → 半天迁
与 node:test 对比
Node 20+ 内置 node --test:
import { test } from 'node:test';
import assert from 'node:assert';
test('add', () => {
assert.strictEqual(add(1, 2), 3);
});
无依赖。简单 unit test 够。
但 mock / snapshot / coverage 弱于 jest / vitest,复杂项目仍 vitest。
踩过的坑
-
vi.mock 提升:vite 把 mock 提升到 file 顶部 → 跟 import 顺序
交互奇怪。简单场景 OK,复杂用 mock factory + lazy。 -
globals: true没设:jest 写法报describe is not defined。 -
CSS import 报错:vitest 不像 jest 自动 mock CSS。配
vitest.config.ts加css: true或css.modules.classNameStrategy。 -
timeout 默认 5s:复杂 e2e 测试超时。
testTimeout: 30000。 -
watch 没 trigger:file change 但 test 没重跑 → vitest cache
bug。vitest --no-cache或 restart。
登录后参与评论。