首页技术专题博客目录我的收藏关于与联系

测试的现实:写测试到底值不值得?

项目要求测试覆盖率 80%,我们写了大半年测试,最后发现大部分测试都是摆设,真正有用的没几个。这篇文章聊聊写测试的真实体验,以及什么时候该写,什么时候不该写。

为什么要写测试?

项目初期,团队决定要写测试,目标是覆盖率 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,不用单元测试
  • 工具函数和工具类,写测试
  • 简单的组件,不写测试

总结

测试不是银弹,不是写得越多越好。关键是要写有用的测试,而不是为了覆盖率凑数。

如果你们项目也在写测试,建议先评估一下测试的价值。如果大部分测试都是摆设,不如删掉,把时间花在更有价值的地方。

测试是工具,不是目标。目标是保证代码质量,如果测试不能达到这个目标,那就换其他方法。