为什么要写测试?
项目初期,团队决定要写测试,目标是覆盖率 80%。理由很充分:保证代码质量、防止回归、提升重构信心。
听起来很有道理,我们就开始写了。用 Jest + React Testing Library,写单元测试和集成测试。
写测试的实际情况
第一个月:热情高涨
第一个月,大家都很积极。每个组件都写测试,每个工具函数都写测试。覆盖率很快就到了 60%。
但问题也来了:写测试的时间比写功能的时间还长。一个简单的组件,写功能 1 小时,写测试要 2 小时。
第二个月:开始应付
第二个月,大家开始应付了。为了凑覆盖率,写了很多没意义的测试。比如测试一个简单的工具函数,就测试它返回什么值,这种测试有什么用?
更糟糕的是,很多测试和实现耦合太紧。改个实现,测试就挂了,但功能其实没问题。这种测试反而成了负担。
第三个月:维护成本
第三个月,维护成本就暴露了。每次改代码,都要改测试。有时候改个很小的功能,要改 10 几个测试文件。
更烦的是,有些测试经常挂,但不是因为代码有问题,而是因为测试本身有问题。比如时间相关的测试,时区不对就挂了。
测试真的有用吗?
我们统计了一下,写了半年测试,真正发现 bug 的测试不到 10%。大部分测试都是:
- 测试实现细节,而不是行为
- 测试简单的工具函数,不会出错的那种
- 测试第三方库的功能,不需要我们测试
- 为了凑覆盖率,写的无效测试
真正有用的测试,是那些测试核心业务逻辑的。比如支付流程、权限校验、数据转换。但这些测试往往很难写,需要 mock 很多东西。
什么时候该写测试?
用了一年多,我觉得这些场景该写测试:
- 核心业务逻辑:比如支付、订单、权限,这些出问题影响大
- 复杂的工具函数:比如数据转换、计算逻辑,容易出错
- 容易回归的功能:以前出过 bug 的地方,写测试防止再出
- 公共库和组件:很多人用的,要保证质量
什么时候不该写测试?
这些场景我觉得不用写测试:
- 简单的 UI 组件:比如按钮、输入框,测试意义不大
- 第三方库的封装:库本身有测试,我们不需要再测
- 经常变的功能:测试维护成本太高
- 原型和实验性功能:还不确定要不要,写测试浪费
我们的策略调整
后来我们调整了策略:
- 不再追求覆盖率,只写有用的测试
- 重点测试核心业务逻辑和容易出错的地方
- UI 测试用 E2E,不用单元测试
- 工具函数和工具类,写测试
- 简单的组件,不写测试
这样调整后,测试数量减少了 60%,但质量提升了。真正有用的测试保留下来,没用的测试都删了。
E2E 测试的体验
我们也试过 E2E 测试,用 Playwright。写起来很爽,能测试真实场景。但问题也很多:
- 运行慢,一个测试要几分钟
- 不稳定,经常因为网络、时间等问题挂掉
- 维护成本高,UI 改一点,测试就要改
- 调试困难,出问题很难定位
最后我们只保留了一些核心流程的 E2E 测试,比如登录、下单。其他的都删了。
测试工具的选择
我们试过几个测试工具:
- Jest:最流行,但配置复杂,运行慢
- Vitest:快很多,但生态还不够成熟
- Playwright:E2E 测试,功能强大但慢
- Cypress:E2E 测试,但只支持 Chrome
现在主要用 Vitest + Playwright,一个做单元测试,一个做 E2E 测试。
如果重新开始
如果重新开始一个项目,我会:
- 不追求覆盖率,只写有用的测试
- 重点测试核心业务逻辑
- UI 测试用 E2E,不用单元测试
- 工具函数和工具类,写测试
- 简单的组件,不写测试
总结
测试不是银弹,不是写得越多越好。关键是要写有用的测试,而不是为了覆盖率凑数。
如果你们项目也在写测试,建议先评估一下测试的价值。如果大部分测试都是摆设,不如删掉,把时间花在更有价值的地方。
测试是工具,不是目标。目标是保证代码质量,如果测试不能达到这个目标,那就换其他方法。