Skip to content

测试分层与回归保护

测试的目的不是追求覆盖率数字,而是让团队能放心修改。最值得自动化的,是容易回归、影响大的业务规则和关键用户流程。

不同层次回答不同问题

层次验证对象特点
单元测试函数、规则、转换逻辑反馈快,定位明确
集成测试模块协作、数据库或接口边界发现真实组合问题
端到端测试用户关键流程贴近真实,但维护成本更高

不必为每个 UI 像素写端到端测试。把规则放在可独立测试的模块中,用少量流程测试确认页面、接口和权限真正协作即可。

从失败风险挑选用例

优先覆盖这些场景:

  • 金额、库存、权限等不能算错或越权的规则。
  • 曾经出现过的缺陷,避免同样问题再次发生。
  • 输入边界:空值、极值、重复提交、过期数据。
  • 失败分支:网络超时、依赖异常、无权限、资源不存在。
js
it('普通成员不能编辑他人文章', () => {
  expect(canUpdateArticle(memberA, articleOfMemberB)).toBe(false)
})

用例名称应描述用户或业务结果,而不是内部方法调用。失败报告才能直接告诉读者“什么行为被破坏了”。

常见错误:只测正常流程,不测失败分支

只写"输入正确、返回正确"的 happy path,是测试最常见的失效模式:

js
// 只测了正常情况
it('创建订单成功', () => {
  const result = createOrder({ userId: 1, amount: 100 })
  expect(result.success).toBe(true)
})

症状:CI 一直全绿,团队对测试信心满满。但上线后 bug 频出——库存不足时没拒绝、重复提交创建了多笔订单、未登录用户居然能下单、金额为 0 或负数时算出了奇怪结果。这些失败分支,测试一个都没覆盖。

为什么错:风险恰恰不在正常流程上——正常路径大家都会手动点一遍。真正出事的是边界和失败:空值、重复、越权、超时、资源不存在。只测 happy path,等于只守了最不容易出问题的门。

正确做法:优先覆盖"错了会出事"的场景,把每个失败分支当作独立用例:

js
it('库存不足时拒绝创建订单', () => {
  expect(() => createOrder({ amount: 100 })).toThrow('INSUFFICIENT_STOCK')
})

it('未登录用户不能创建订单', () => {
  expect(() => createOrder({ amount: 100 })).toThrow('UNAUTHORIZED')
})

it('金额必须为正数', () => {
  expect(() => createOrder({ amount: -1 })).toThrow('INVALID_AMOUNT')
})

问自己一句:"如果这个输入是空的、非法的、恶意的,代码会怎样?" 答不上来的地方,就是该补测试的地方。

让测试可重复

测试数据需要独立、可预测,并在结束时清理。时间、随机数、网络和第三方依赖应可替换或固定,否则测试会因环境抖动而失去信任。

不稳定来源常用处理
当前时间注入时钟或固定时间
外部接口使用契约 mock 或测试环境
随机数据固定种子或显式样例
共享数据库每个用例独立准备与回收数据

测试不是发布后的借口

自动化通过不代表没有风险。上线前仍需确认配置、权限、数据迁移和监控;上线后把真实故障转化为下一条测试或约束,测试集才会持续变得有价值。

为复用而记录,为理解而整理。