单元测试与集成测试对比
iOS 测试策略的基石:是选择让单元隔离的单元测试,还是选择验证组件间交互的集成测试?如何正确搭建测试金字塔?
以 Vite 自身速度运行的原生 ESM/TS 测试运行器
拥有 13 年历史、成熟稳定,且是 React Native 的默认选择
在基于 Vite 或 Nuxt 的新项目中,默认应该选择 Vitest:你可以直接共享同一个 vite.config.js。在 Next.js 中配置会保持独立(因为它的编译器不是 Vite),但你仍然可以在不额外安装 transform 包的情况下获得原生 ESM/TypeScript 支持。 但如果你已经有一个运行良好的大型 Jest 测试套件,尤其是依赖 React Native/Metro 的场景,迁移成本可能会超过收益。可以选择第三条路:新测试用 Vitest 编写,现有套件继续留在 Jest 中——两者完全可以在同一个 monorepo 中共存。
| 分类 | Vitest 5 | Jest |
|---|---|---|
| 性能 | 9/10 | 7/10 |
| 学习难易度 | 7/10 | 7/10 |
| 生态系统 | 6/10 | 9/10 |
| 社区 | 6/10 | 9/10 |
| 就业市场 | 6/10 | 8/10 |
| 面向未来 | 9/10 | 6/10 |
// Vitest 5 - 购物车合计计算函数的测试
// vitest.config.ts 与 vite.config.ts 共享;无需额外 transform
import { describe, it, expect, vi } from 'vitest'
import { calculateCartTotal } from './cart'
import { fetchDiscount } from './discount-api'
vi.mock('./discount-api')
describe('calculateCartTotal', () => {
// Vitest 5:clearMocks 默认值为 true,无需额外的清理调用
it('indirim kodu olmadan ara toplami dogru hesaplar', () => {
const items = [
{ price: 129.9, qty: 2 },
{ price: 49.5, qty: 1 },
]
expect(calculateCartTotal(items)).toBeCloseTo(309.3, 2)
})
it('gecerli indirim kodunda vi.when ile kosullu mock kullanir', async () => {
// Vitest 5 新 API:根据参数返回不同的 mock 行为
vi.when(fetchDiscount).calledWith('SEPET10').thenResolve({ percent: 10 })
vi.when(fetchDiscount).calledWith('GECERSIZ').thenReject(new Error('invalid code'))
const items = [{ price: 100, qty: 1 }]
const total = await calculateCartTotal(items, 'SEPET10')
expect(total).toBeCloseTo(90, 2)
expect(fetchDiscount).toHaveBeenCalledWith('SEPET10')
})
it('gecersiz kod hata firlatir', async () => {
vi.when(fetchDiscount).calledWith('GECERSIZ').thenReject(new Error('invalid code'))
const items = [{ price: 100, qty: 1 }]
await expect(calculateCartTotal(items, 'GECERSIZ')).rejects.toThrow('invalid code')
})
})
// Monorepo:vitest -p web --coverage(仅运行 'web' 项目 + 生成覆盖率报告)// Jest 30 - 购物车合计计算函数的测试
// 需要另外配置 babel.config.js 或 ts-jest transform 链
const { calculateCartTotal } = require('./cart')
const { fetchDiscount } = require('./discount-api')
jest.mock('./discount-api')
describe('calculateCartTotal', () => {
afterEach(() => {
jest.clearAllMocks() // 在 Jest 中不是默认行为,需要手动调用
})
it('indirim kodu olmadan ara toplami dogru hesaplar', () => {
const items = [
{ price: 129.9, qty: 2 },
{ price: 49.5, qty: 1 },
]
expect(calculateCartTotal(items)).toBeCloseTo(309.3, 2)
})
it('gecerli indirim kodunda mockResolvedValue kullanir', async () => {
fetchDiscount.mockImplementation((code) => {
if (code === 'SEPET10') return Promise.resolve({ percent: 10 })
return Promise.reject(new Error('invalid code'))
})
const items = [{ price: 100, qty: 1 }]
const total = await calculateCartTotal(items, 'SEPET10')
expect(total).toBeCloseTo(90, 2)
expect(fetchDiscount).toHaveBeenCalledWith('SEPET10')
})
it('gecersiz kod hata firlatir', async () => {
fetchDiscount.mockRejectedValue(new Error('invalid code'))
const items = [{ price: 100, qty: 1 }]
await expect(calculateCartTotal(items, 'GECERSIZ')).rejects.toThrow('invalid code')
})
})
// 使用 jest --projects packages/web --coverage 在 monorepo 中仅运行 'web' 包在基于 Vite 或 Nuxt 的新项目中,默认应该选择 Vitest:你可以直接共享同一个 vite.config.js。在 Next.js 中配置会保持独立(因为它的编译器不是 Vite),但你仍然可以在不额外安装 transform 包的情况下获得原生 ESM/TypeScript 支持。 但如果你已经有一个运行良好的大型 Jest 测试套件,尤其是依赖 React Native/Metro 的场景,迁移成本可能会超过收益。可以选择第三条路:新测试用 Vitest 编写,现有套件继续留在 Jest 中——两者完全可以在同一个 monorepo 中共存。
获取免费咨询不会,并非一对一取代。两者在 2026 年 9 月都在积极开发中:Vitest 5.0(2026 年 9 月 3 日)和 Jest 30.5.2(2026 年 9 月 18 日)。在基于 Vite 和 Nuxt 的项目中,由于 Vitest 复用了应用自身的 transform 管线,配置成本更低;而 Jest 在 React Native/Metro 以及现有大型代码库中仍将继续使用。