• debug 式开发所带来的时间浪费问题
  • 解决方案:使用 TDD 开发解决
  • TDD 的前提是完善的业务理解和有效的测试策略
  • 给出一个完整的测试策略

你的时间浪费在哪?

很多人在开发软件的时候,习惯于先在本地启动服务,然后在每一次代码变更后,等待服务热更新结束。然后在前端界面,或者 postman 中,手动点击验证一下。这种试一下 - 点一下 - 调整一下的开发模式,和我们拿到一个复杂的 bug 后不断的试错修复并没有什么区别,其实是一种很典型的 Complex 行为模式,我们并不知道我们写的代码是否满足需求,也不知道代码是否影响到了哪些模块。这种开发方式,导致我们在各种地方不断地浪费时间:

  1. 缺乏规划:频繁的修改代码、手动验证,往往意味着代码设计缺乏正确的规划。而缺乏规划,又反过来增加了未来修改代码的成本。例如,当需求变更时,开发者需要重新理解代码逻辑,甚至可能因为模块耦合度过高而被迫重构。
  2. 重复劳动:由于我们并不知道改动会影响到什么地方,我们会重复花时间在反复出现的问题上。比如,修复一个接口参数校验问题后,可能会因为其他模块的隐式依赖,导致另一个功能异常,需要重新调试。
  3. 思路打断:我们不断在中断代码思路,去间断性的验证可能出现的问题,而思路重新回到正轨上,则是一件非常耗时的事情。心理学研究表明,开发者每次中断后需要平均 15 分钟才能恢复深度工作状态。

我们直觉上浪费时间的 TDD ,反而是最高效的 clear 模式。我们在彻底理解业务需求的情况下,我们可以通过编写测试用例,清楚规划出我们要实现什么样的功能。而去实现功能,我们的目标也很明确,仅仅是让测试用例通过罢了,测试用例要么不过、要么过。只要已有的测试用例并没有失败,我们就可以不用考虑是否影响到别的功能去编写剩下的功能。

有这么一个场景,我们的 Commit 中编写了完善的测试,但这个 Commit 还是在 QA 阶段导致了预期外的问题,那么写测试是不是没有意义了呢?其实并不是,首先这个问题并不会因为你没写测试就不发生,其次恰恰就是因为旧有的代码的测试覆盖率不足,才导致我们没有在开发阶段就发现这个问题。这种在 QA 之前,尽早验证并暴露问题的模式,也就是我们平时所说的测试左移。通过 TDD,我们实际上将质量保障的防线从 QA 阶段前移到开发阶段,让问题更早暴露、修复成本更低。

TDD: 开发质量保障的第一道防线

TDD 的全称为 Test-Driven Development。对于初次接触到 TDD 的人来说,都会觉得这是一个非常奇怪甚至难以理解的开发模式。

  1. 测试是 QA 的活,即使我测了他们也得测,那我还写测试干啥?
  2. 业务那么赶,哪来的时间写测试,那不是影响我效率吗?
  3. 代码能跑起来就行了,我不写测试不也干了那么多年吗,那干嘛还要花时间去写?

在回答这些问题之前,我们不如先思考另一个更加重要的问题。作为我们本职工作写出来的代码质量真的有保障吗?

我想大多数人都会回答“是”,但是我们多数人理解的代码质量,一般指的是代码的架构设计,算法的合理之处,命名的可读性等等,却很少考虑代码的可验证性。在每次提交之前,我们并不能保证我们的代码是否会带来预期外的更新。对于 QA 来说,他们也不可能把所有相关的功能都给你验证一遍。最终的结果就是,直到生产环境上,我们代码的破坏性更新才会被客户发现,这就是我们多数 bug 的来源。仅仅通过 QA 团队带来的所谓交付质量,最多也只是一个给上级的虚假的美好愿景罢了

Test is Task

TDD 的核心流程的第一步,不是如何去写测试,而是先把需求分解为一系列可验证的里程碑点。从这个角度来说,TDD 也可以被称为 Task-Deiven Development。如果一个开发人员在读完 PRD 和并且和产品交流后,但是写不出测试。无非是以下几种情况。

  1. 没接触过真的不会写测试,这个很好解决,测试框架的的 API 都很简单,基本上学下断言语句和如何 mock 数据以及构造 stub。一两天时间就能上手。
  2. 遗留系统实在太复杂了,根本不知道在哪个地方写测试。我们在上一节用充血模型从遗留系统重构出一个新模块就是为了解决这个问题。
  3. 完全没有理解需求,如果没理解需求。这时就是