浅谈测试驱动开发(TDD)

TDD 是什么

TDD 是敏捷开发中的一项核心实践和技术,也是一种设计方法论。TDD 的原理是在开发功能代码之前,先编写单元测试用例代码,测试代码确定需要编写什么功能代码。

TDD 的基本思路是通过测试来推动这个开发的过程,但测试开发并不是单纯的测试工作,而是把 需求分析,设计,质量控制量化的过程。

TDD 的目的不仅仅是测试软件,保证代码质量仅仅是其中的一部分,更重要的是,在开发过程中帮助开发者除去模棱两可的需求。

Kent Beck 的著作《测试驱动开发》中说:

“测试驱动开发不是一种测试技术。它是一种分析技术、设计技术,更是一种组织所有开发活动的技术”。

为什么要用 TDD

通常情况下,我们都习惯在需求分析完成后,尽快投入到功能代码的编写工作中,之后再调试。而 TDD 则不太一样,它假设我们已经有一个 ”测试用户“ 了,它是功能代码的第一个使用者,尽管功能还不太完善。

当我们站在 ”测试用户“ 的角度去写测试代码的时候,我们需要考虑的是,这个 ”测试用户“ 改如何使用功能代码呢?是通过一个类直接调用呢,还是构建类的实例去掉用方法呢?这个方法又如何传参?方法如何命名,方法有返回值吗?

有了测试代码之后,我们的需求应该也已经明确了,这时候我们针对测试代码编写功能代码,就是为实际需求编写功能代码,并且要以最快的速度让测试由 ”红“ 变为 ”绿“ 。(此时代码可能很不优雅,不过没关系)。

当测试代码通过之后,我们就可以放心大胆的对功能代码进行 ”重构“ 了 ---- 优化原来比较丑陋,臃肿,设计不佳的代码。(参考《重构》一书)。

TDD 意味着持续测试,持续重构,能够提升团队代码质量, 让我们对自己的代码充满信心(其实就是安全感)。

不想 TDD 的人,要么脑袋足够清醒,对业务需求了然于胸,能够心算测试的各种 Case 和输入输出,要么,就是摸着石头过河 – 走一步算一步。

TDD 主要是确保两件事:

  • 确保所有的需求都能被照顾到
  • 在代码不断增加和重构的过程中,可以检查所有的功能是否正确

TDD 是在间接考验你的设计能力。并且大量减少你的返工时间。

为了遵循 TDD 而写的代码,容易进入一个误区: 代码是为了满足测试而用的,而忽略了实际的需求。

初接触 TDD 的人常犯下面的错误:

  1. 在声明测试方法后,便开始写实现代码;
  2. 写完 “所有” 的测试代码才开始写实现;
  3. 一次实现过多的代码(超出当前测试覆盖的业务);
  4. 从不重构;
  5. 测试实现细节而不是接口行为;

TDD 的基本流程

TDD 的一般步骤是:红灯 – 绿灯 – 重构

浅谈测试驱动开发(TDD)

基本过程可以拆分为下面六个步骤:

  1. 分析需求,把需求拆分为具体任务。
  2. 从列表中取出一个任务,对其编写测试用例。
  3. 由于还没有编写实际的代码,此时测试代码不可能通过(红)。
  4. 编写对应的功能代码,尽快让测试通过(绿)。
  5. 对代码进行重构,并保证测试通过。
  6. 重复以上步骤。

TDD 比较关键的一步在于如何写出有效的测试代码,这里有四个原则可以参考:

  1. 测试应该模拟正常的使用过程。
  2. 应该尽量做到分支覆盖。
  3. 测试数据应该尽量包括真实数据,以及边界数据。
  4. 测试语句和测试数据应该尽量简单,容易理解。

Is TDD Dead?

TDD 自从被 Rails 大神、创始人 David Heinemeier Hansson 于 2014 年 提出,激起千层浪。各种人对此褒贬不一,Kent Beck、Martin Fowler、David Hansson 三人就这梗举行的系列对话(辩论),有视频和文字,非常精彩,值得细读。Is TDD Dead?
浅谈测试驱动开发(TDD)
浅谈测试驱动开发(TDD)

但是不管怎样,我们需要知道,TDD 不是银弹,它是一种开发方式,要区分适用 TDD 的场合,不使用 TDD 我们也能开发出工作良好的软件。

对我们大多数人来说,TDD 解决了:

1)我要开始写代码了,我从哪开始搞? 搞个 main,还是 test?

2)这个功能有点复杂,接口测试起来太麻烦,先搞个 UT。

3)我不知道那个家伙的代码写的怎么样,以后会不会捅个篓子,要求他 TDD 好了,CodeReview 的时候也轻松点。

还有一种人:飞机上写几个小时代码,下来编译一遍过还没有 Bug 的人。你跳出来说,他没有 TDD,他 LowB,那你怕不是有毛病。

总之一句话,不要听别人说 TDD 好不好,自己试试再说。之前参加过熊杰(《重构》译者)的一个 TDD训练营,他经常在群里面说一句话,并且我在参加完训练营后也十分认同,就是说,TDD 的感觉是靠练出来的。看再多的书,不如自己亲自多练几遍,就能找到答案。

启发

在我们的 HIS 系统中,由于业务已经十分复杂,代码量也十分庞大,想要使用好 TDD 并不容易,但是在新系统中,我们完全可以考虑实践 TDD ,因为由于微服务拆分带来的好处,每个微服务的复杂度和代码量已经变得很少,独立的业务需求,更便于我们先编写测试,再实现代码。不久我们可能就会发现,生活变得美好起来。