TDD 之让我们再聊聊 TDD(终)-- 正其思,规其行
起因
在前两篇《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 都可以按照这个三步来实施:
变红:写一个不通过的测试 (红)
变绿:写实现代码,使其刚好 通过测试 (绿)
重构
三原则
业界元老 Robert C. Martin 也提出过他总结的 TDD 三原则:
不允许编写任何产品代码,除非目的是为了让失败的测试通过
不允许编写多于一个的失败测试,编译错误也是失败
不允许编写多于恰好能让测试通过的产品代码,有效的减少返工
这三个原则很好的总结了 TDD 实践的关键步骤。此三原则虽然是正确的,但是严格按照此三个原则去做却是不易的,并且在现实的开发工作中并不是很多人能严格按照此三原则去编写代码,因为转变思维是一件很困难的事情,而且如果在时间短交付压力大的情况下思维转换就更为困难了。
总结
所以在 TDD 的实施过程中一定要着重思考并实施 TDD 中的 Test,而不能仅仅只是 Development,不然很可能带来大量的维护工作,从而出现了大家讨厌的 TDD。
在我们经历的项目中,我们一般以 ATDD 结合 UTDD 的模式进行工作,但是也有全部使用 UTDD 的项目,也有全部是基于 ATDD 的项目。所以不同的项目可以根据资源的不同来决定其比例,从而尽最大可能去保证软件的内在质量和业务正确性。
版权声明: 本文为 InfoQ 作者【刘冉】的原创文章。
原文链接:【http://xie.infoq.cn/article/db1ea9f73318065cf6059bbea】。文章转载请联系作者。
评论