TDD已死?如何进行有效的TDD实践 | IDCF
来源:ThoughtWorks洞见、刘冉的思辨悟
作者:刘冉
一、TDD已死?
当前国内对 TDD 的理解十分模糊,大部分人也没有明确和有意识的去实施 TDD,因此许多人对此都有着不同的理解。其中最经典的理解就是基于代码的某个单元,使用 Mock 等技术编写单元测试,然后用这个单元测试来驱动开发,抑或是帮助在重构、修改以后进行回归测试。而现在大部分反对 TDD 的声音就是基于这个理解,比如:
- 工期紧,时间短,写 TDD 太浪费时间;
- 业务需求变化太快,修改功能都来不及,根本没有时间来写 TDD;
- 写 TDD 对开发人员的素质要求非常高,普通的开发人员不会写;
- TDD 推行的最大问题在于大多数程序员还不会「写测试用例」和「重构」;
- 由于大量使用 Mock 和 Stub 技术,导致 UT 没有办法测试集成后的功能,对于测试业务价值作用不大;
- ......
- 首先是先写测试,这里的测试并不只是单元测试,也不是说一定要使用 mock 和 stub 来做测试。这里的测试就是指软件测试本身,可以是基于代码单元的单元测试,可以是基于业务需求的功能测试,也可以是基于特定验收条件的验收测试。
- 其次是帮助开发人员,主要是帮助开发人员理解软件的功能需求和验收条件,帮助其思考和设计代码,从而达到驱动开发的目的。
- ATDD(Acceptance Test Driven Development):验收驱动测试开发,首先 BA 或者 QA 编写验收测试用例,然后 Dev 通过验收测试来理解需求和验收条件,并编写实现代码直到验收测试用例通过。
- UTDD(Unit Test Driven Development):单元驱动测试开发,首先 Dev 编写单元测试用例,然后编写实现代码直到单元测试通过。这个就是现在很多人所谓的 TDD、实践的 TDD、喜欢的 TDD、抱怨的 TDD,但是它却只是真正意义上 TDD 的一部分而已。
二、TDD的实施和分层
TDD 的实施一般分为思维层面和技术层面。一般来说,思维层面上的实施成本较低、容易接受,但是缺点很多,比如难以传递、难以持续获得快速反馈等;而技术层面上的实施一般成本较高、不容易被人接受,但是优点更多,比如可以获得快速反馈、更容易传递和协作等。而现实世界中 TDD 的实施一般分为三个阶段,即无意识的 TDD、被动通过技术实现的 TDD、以及有意识和主动通过技术实现的 TDD。第一阶段:无意识的 TDD对于软件开发人员,当他们拿到一个新的软件需求时,首先会思考如何实现,其中包括当前软件架构、业务分解、实现设计、代码分层、代码实现等。然后通过思考和设计所得到的产出物来驱动代码实现,进而在代码实现中会思考如何通过一个或多个函数或者算法来实现业务逻辑。所以软件系统的实现要先通过意识层面的思考,再进行技术层面的工作。当开发人员思考和设计这些函数或者方法的时候,一般都会思考它们有哪些参数,然后想象将这些参数换成真实的数据后传递进去,会得到怎样的返回值。好一点的开发人员会思考如何处理异常输入和异常返回值。这类思考其实已经是意识思维上的 TDD,它帮助开发人员先在大脑里面设计并验证代码实现,甚至帮助其重构代码。所以很多开发人员都在无意识的情况下做着 TDD。比如在一个银行系统里面,开发人员拿到一个需求,需要开发一个通过手机 APP 转账的功能。
- 首先开发人员会基于当前的软件架构思考:是开发一个全新的模块来处理这个业务?还是基于当前架构中的某个模块来添加代码进行处理?
- 当确定架构和设计之后,就开始思考具体的代码实现,比如类的设计、方法的设计或者函数的设计等。当开发“将钱从原帐号转出”这个功能前,开发人员会思考:这个功能需要支持当钱从原帐号中转出成功后,原帐号中的余额等于原始余额减去转出金额。进一步有些程序员还会设计一些用来验证功能的实例,比如帐号中的原始余额是 999.99,转出 111.11,那么剩余的金额就应该是 888.88。
- 在这样思考之后,开发人员便开始根据自己大脑中的测试逻辑和用例来驱动和辅助开发过程。在代码开发完毕之后还会想一些办法来验证一下所实现的功能是否符合预期,比如人工使用之前的或者新的测试用例再测试一下。如果验证正确,就会认为自己开发的功能正确了,并交给测试人员进行测试。
三、TDD实践
- 学好TDD靠多多练习就可以了,不用学习其理论知识;
- 开发应该自己理解业务,并提炼测试(需求)点来实施TDD;
- 开发只要做好TDD,就不需要其他人测试了;
- 软件没有做好就是因为TDD没有做好;
- 等等。
- 首先,TDD不是银弹,所以软件没有做好的原因是很多的,也不是靠TDD做好了就一定能做好。
- 其次,学习TDD的理论是非常重要的,而仅仅靠不断练习并自悟的方法,对于大部分普通人来说是难以成功的。
- 最后,一般开发能做的较好的TDD,一般是UTDD,而对于ATDD,则需要相应的业务分析,测试分析与设计的相关方法和技术。如果要求开发做好ATDD,则需要开发学习并掌握相应的业务分析,测试分析与设计的相关方法和技术。而这对于开发也是非常大的挑战。所以在真正的TDD实践中,如果想大规模实施TDD,仍然需要相应的分工才更可行,更容易实施。而提前充分学习并了解TDD实践的相关理论,也是可以帮助实施人员更好的去实践,少踩坑,从而正其思规其行。
- 测试前移(左移)的思维能力
- 软件设计能力
- 业务和技术需求分析和任务拆分能力
- 测试用例分析和设计能力
- 自动化测试开发能力
- 代码重构和持续改进能力
- 关注单元级别的代码设计
- 测试用例需要明确的实例
- 清晰的单元完成标志
- 最快的feedback周期
- 有效的减少开发过程中side effect引起的返工
- 可以帮助开发减少调式的成本
- 可以作为单元接口的使用文档
- 关注业务价值,测试与需求一体化
- 明确的测试实例(SBE)而不是复杂的描述
- 清晰的功能完成标志
- 更快的feedback周期,提早并频繁沟通
- 消除误解,减少返工
- 可视化的验收回归测试
- 可以作为描述功能的活文档
- 变红:写一个不通过的测试 (红)
- 变绿:写实现代码,使其刚好通过测试 (绿)
- 重构
四、最后
- 不允许编写任何产品代码,除非目的是为了让失败的测试通过;
- 不允许编写多于一个的失败测试,编译错误也是失败;
- 不允许编写多于恰好能让测试通过的产品代码,有效的减少返工。
评论