起因

在前两篇《TDD之让我们再聊聊TDD》 和《 TDD之让我们再聊聊TDD(续)》 中我们聊了很多TDD理论和实践相关的疑惑,其中包括TDD的分类,选择以及其实施步骤。最近TDD相关的培训和讨论也越来越多,还提出了很多独特的观点,比如:

  • 软件没有做好就是因为TDD没有做好
  • TDD没有做好所以软件也做不好
  • 学好TDD靠多多练习就可以了,不用学习其理论知识
  • 开发应该自己理解业务,并提炼测试(需求)点来实施TDD
  • 开发只要做好TDD,就不需要其他人测试了
  • 等等

对于这些观点我有不同的看法,比如我在第一篇讨论中就提到,TDD不是银弹,所以软件没有做好的原因是很多的,也不是靠TDD做好了就一定能做好。其次学习TDD的理论是非常重要的,而仅仅靠不断练习并自悟的方法,对于大部分普通人来说是难以成功的。最后一般开发能做的较好的TDD,一般是UTDD,而对于ATDD,则需要相应的业务分析,测试分析与设计的相关方法和技术。如果要求开发做好ATDD,则需要开发学习并掌握相应的业务分析,测试分析与设计的相关方法和技术。而这对于开发也是非常大的挑战。所以在真正的TDD实践中,如果想大规模实施TDD,仍然需要相应的分工才更可行,更容易实施。而提前充分学习并了解TDD实践的相关理论,也是可以帮助实施人员更好的去实践,少踩坑,从而正其思规其行。

能力

在真实工作中要较好的实施TDD需要具备以下能力:

  • 测试前移(左移)的思维能力
  • 业务和技术需求的分析和任务拆分能力
  • 测试用例设计能力
  • 自动化测试开发
  • 能力代码重构
  • 能力持续改进的能力

首先是测试前移(左移)的思维能力是TDD实施的前提条件。如果没有这个思维能力,或者不认可这个测试前移(左移)的价值,这样的开发人员则很难认可TDD的开发方式,从而可能会想尽各种办法抵制TDD,或者阳奉阴违,从而导致TDD实施艰难,并显得困难重重。所以一定要拥有这个能力作为前提才容易真正实施好TDD。

要实施TDD,第一步就是业务需求和技术需求的分析,以及任务拆分能力。其次是根据需点或者任务点设计测试用例的能力。这里面包含了一个业务需求人员和测试人员的基本能力。其中业务需求分析能力需要能较好的理解并分析已有的业务需求,并能总结出业务需求和任务验收点。然后测试用例设计能力需要能根据业务的需求和业务验收点设计出有效的正确的测试用例,从而才能驱动出符合业务需求的代码。

对于普通人来讲,成功实施TDD最为核心的两个能力则是自动化测试开发能力和代码重构能力。其中自动化测试开发能力是指熟练使用各种自动化测试框架,将前面设计出来的测试用例自动化起来。对于UTDD常用的自动化测试框架由JUnit,Jasmine等,而对于ATDD常用的自动化测试框架则由Cucumber,RobotFramework等。只有将测试用例自动化之后才能快速的进行回归测试,从而帮助代码重构。而良好的代码重构能力,则是代码质量内建,防止代码腐化以及保障代码易于维护的主要手段之一。如果没有能力或者不愿对代码进行重构,那么就很难持续实施的TDD。

最后要实施一套完整的好的TDD,还需要一个持续改进的能力。不仅需要对代码进行持续改进,即代码重构;还需要对自动化测试的代码,测试用例设计和业务分析进行持续改进。只有这样对TDD的各个步骤和环节都进行持续改进,才能越来越好的实施TDD。

实施

在实际工作中,实践TDD第一步就是转变思维-测试前移(及测试左移),将测试用例分析,设计和实现前移到编写代码之前。这里的测试并不只是单元测试,也不是说一定要使用mock和stub来做测试。这里的测试就是指软件测试本身,可以是基于代码单元的单元测试,也可以是基于业务需求的功能测试,也可以是基于特定验收条件的验收测试。其次是帮助开发人员,主要是帮助开发人员理解软件的功能需求和验收条件,帮助其拆分任务,思考和设计代码,从而达到驱动开发的目的。所以TDD可以帮助开发人员分析和理解需求 ,并且有效的减少过度设计,获得大量有效的测试用例(手动/自动), 以及可以获得快速反馈,从而有效的减少返工,提高代码的内在质量。

UTDD

对于UTDD(单元测试驱动开发),首先由开发人员自己,或者开发人员互相结对业务,或者和测试人员一起分析并拆分任务验收单元。然后开发人员针对每个任务验收单元编写自动化单元测试用例,并实现代码直到单元测试通过。任务验收单元主要针对函数(方法)单元或者业务模块单元代码,而且测试一定是自动化测试,这样才能在开发的过程中快速的反复的执行它们,从而达到驱动开发的目的。还可以在持续集成流水线中快速反复的执行他们,从而帮助持续集成获得单元测试层面上的快速反馈。

UTDD的特点如下:

  • 关注单元级别的代码设计
  • 测试用例需要明确的实例
  • 清晰的单元完成标志
  • 最快的feedback周期
  • 有效的减少开发过程中side effect引起的返工
  • 可以帮助开发减少调式的成本
  • 可以作为单元接口的使用文档

ATDD

而对于ATDD(验收测试驱动开发),首先业务分析人员或者测试人员编写验收测试用例,然后开发通过验收测试来理解需求和验收条件,并编写实现代码直到验收测试用例通过。由于验收方法和类型也是多种多样的,所以根据验收方法和类型的不同ATDD又可以分为BDD(Behavior Driven Development),EDD(Example Driven Development),FDD(Feature Driven Development),CDCD(Consumer Driven Contact Development)等具体的实践方法。比如以用户使用软件时软件的行为为验收标准,这个是BDD;如果以特定的实例数据为验收标准,这个是EDD;如果以Web Service API消费者提出API契约来驱动API开发,这个是CDCD等。所以ATDD的具体实现需要结合项目的实际情况来选用适合的验收测试的方法与类型。

ATDD的特点如下:

  • 关注业务价值,测试与需求一体化
  • 明确的测试示例(SBE)而不是复杂的描述
  • 清晰的功能完成标志
  • 更快的feedback周期,提早/频繁沟通
  • 消除误解,减少返工
  • 可视化的验收回归测试
  • 可以作为描述功能的活文档

最后TDD测试驱动开发经典三步曲,不论是UTDD还是ATDD都可以按照这个三步来实施:

  1. 变红:写一个不通过的测试 (红)

  2. 变绿:写实现代码,使其刚好 通过测试 (绿)

  3. 重构

三原则

业界元老Robert C. Martin 也提出过他总结的TDD三原则:

  1. 不允许编写任何产品代码,除非目的是为了让失败的测试通过
  2. 不允许编写多于一个的失败测试,编译错误也是失败
  3. 不允许编写多于恰好能让测试通过的产品代码,有效的减少返工

这三个原则很好的总结了TDD实践的关键步骤。此三原则虽然是正确的,但是严格按照此三个原则去做却是不易的,并且在现实的开发工作中并不是很多人能严格按照此三原则去编写代码,因为转变思维是一件很困难的事情,而且如果在时间短交付压力大的情况下思维转换就更为困难了。

总结

所以在TDD的实施过程中一定要着重思考并实施TDD中的Test,而不能仅仅只是Development,不然很可能带来大量的维护工作,从而出现了大家讨厌的TDD。

在我们经历的项目中,我们一般以ATDD结合UTDD的模式进行工作,但是也有全部使用UTDD的项目,也有全部是基于ATDD的项目。所以不同的项目可以根据资源的不同来决定其比例,从而尽最大可能去保证软件的内在质量和业务正确性。