单元测试应隔离外部依赖、覆盖边界值、验证单一行为;集成测试则验证模块协作链路,用 msw 拦截请求、聚焦数据流与副作用,二者必须共存。

单元测试只测一个函数,且必须切断所有外部依赖
比如你写了个 calculateTotal 函数,它内部调用了 fetch 或读了 localStorage,那它就不是“可单元测试”的——得先抽离副作用,再单独验证计算逻辑。真实项目里最常踩的坑是:没 mock 掉 API 调用,结果测试偶尔失败,你以为是代码问题,其实是网络抖动。
- 必须用
jest.mock('axios')或jest.fn()替换掉所有外部调用 - 边界值要覆盖:空数组、
null、负数、极大数,不能只测“正常情况” - React 组件推荐用
@testing-library/react的render+screen,别用shallow—— 一改 props 结构,全红 - 异步函数必须显式处理:用
async/await+expect(...).resolves,否则 Jest 会跳过断言
集成测试不 mock 接口,但也不连真实后端
集成测试的目标是验证“按钮点下去 → 请求发出去 → 数据更新到 UI”这一整条链路是否通,而不是测单个函数对不对。它比单元测试慢,但能暴露接口字段错位、状态没同步、事件没触发等协作类问题。
- 用
msw拦截请求并返回预设响应,既不用启动真实服务,又保留了真实 fetch 行为 - 允许真实 DOM 操作:用
userEvent.click()、screen.findByText()等等待异步渲染完成 - 别在集成测试里重复测分支逻辑(那是单元测试干的),重点盯数据流和副作用:比如登录后
authToken是否写进 context?路由是否跳转? - Cypress 更适合页面级流程(如注册→邮箱验证→登录),Vitest + msw 更适合组件+Hook组合逻辑(如
useCart+useApi)
Jest 单元测试怎么写才不白写
很多人写了测试却不敢删、不敢改,因为一动就红——说明测试耦合了实现细节,比如断言了某个内部变量名,或校验了组件内部 div 的 class 名。真正有用的单元测试,应该描述行为,而不是结构。
- 测试名要像一句人话:
should calculate final price with tax when isVip is true,而不是test1 - 每个
it只验证一个明确行为,别在一个用例里塞多个断言 - mock 要校验参数:不仅
fetchUser.mockResolvedValue(...),还得expect(fetchUser).toHaveBeenCalledWith('/api/users/123') - 避免
setTimeout等真实计时器,改用jest.useFakeTimers()+jest.runAllTimers()
别把集成测试写成“端到端测试”
很多团队误以为集成测试就得开浏览器、等动画、截图、查元素坐标——其实大可不必。只要在 JSDOM 环境中能跑通“触发事件 → 发请求 → 更新 DOM → 断言内容”,就已经是合格的集成测试了。真上 Cypress 是为了捕获跨 iframe、Service Worker、真实网络延迟等问题,不是为了“看起来更高级”。
立即学习“Java免费学习笔记(深入)”;
- 过度 mock = 退化成单元测试;完全不 mock = 依赖环境,CI 随机失败
- 集成测试失败时定位困难,所以优先覆盖核心路径:下单、支付回调、表单提交成功反馈
- Cypress 默认不支持 ESM,遇到
import.meta.url报错,得在cypress.config.ts里关掉supportFile并手动引入初始化逻辑
最常被忽略的一点:单元测试是地基,集成测试是承重墙。没有前者,后者失败时你得花半小时排查到底是哪个模块出的问题;只有前者,你永远不知道两个模块拼在一起会不会互相拖垮。它们不是二选一,而是必须共存的两种节奏。











