什么是TDD

定义

一种不同于传统软件开发流程的新型的开发方法。他要求在编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,通过测试来推动整个开发的进行。这有助于编写简介可用和高质量的代码,并加速开发过程。

出处

测试驱动开发出自极限编程(Extreme Programming,XP)

  • 个人开发者的实践

    结对编程(Pair programming)

    测试驱动开发(Testing-Driven Development)

    重构(Refactoring)

    简单设计(Simple Design)

  • 团队层面的实践

    代码集体所有权(Collective Code Ownership)

    编码规范(Code Standards)

    系统隐喻(System Metaphor)

    持续集成(Continuous Integration)

    可持续的开发节奏(sustainable pace)

  • 项目交付层面的实践

    小型发布(Small Release)

    客户测试(Customer Tests)

    团队协作(Whole Team)

    规划策略(The Planning Game)

广义与狭义的TDD

TDD有广义和狭义之分

  • 狭义的TDD: UTDD(Unit Test Driven Development)

  • 广义的TDD: ATDD(Acceptance Test Driven Development), 包括BDD(Behavior Driven Development)和 Consumer-Driven Contracts Development 等。

TDD的三层含义

  • Test-Driven Development,测试驱动开发

  • Task-Driven Development,任务驱动开发,要对问题进行分析并进行任务分解

  • Test-Driven Design,测试保护下的设计改善。TDD 并不能直接提高设计能力,它只是给你更多机会和保障去改善设计

为什么要TDD

传统的编码方式

  • 需求分析,想不清楚细节,管他呢,先开始写

  • 发现需求细节不明确,去跟业务人员确认

  • 确认好几次终于写完所有逻辑

  • 运行起来测试一下,靠,果然不工作,调试

  • 调试好久终于工作了

  • 转测试,QA 测出 bug,debug, 打补丁

  • 终于,代码可以工作了

  • 一看代码烂的像坨屎,不敢动,动了还得手工测试,还得让 QA 测试,还得加班…

TDD编码方式

  • 先分解任务,分离关注点

  • 列Example,用实例化需求,澄清需求细节

  • 写测试,只关注需求,程序的输入输出,不关心中间过程

  • 写实现,不考虑别的需求,用最简单的方式满足当前这个小需求即可

  • 重构,用手法消除代码里的坏味道

  • 写完,手动测试一下,基本没什么问题,有问题补个用例,修复

  • 转测试,小问题,补用例,修复

  • 代码整洁且用例齐全,信心满满地提交

TDD的好处

  • 效率更高

    提前确认需求,减少开发中的中断和等待

    小步快走,节省调试时间

  • 质量有保证

    高测试覆盖率

    自动回归测试

如何TDD

TDD的基本流程是:

  1. 快速新增一个测试用例

  2. 运行所有的测试(有时只需要运行一个或一部分),发现新增的测试不通过

  3. 做一些小小的改动,尽快地让测试程序可运行,为此可以在程序中使用一些不合情理的方法

  4. 运行所有的测试,并且全部通过

  5. 重构代码,以消除重复设计,优化设计结构

注意事项

  • 合理拆分任务

    TDD之前要拆分任务,把一个大需求拆成多个小需求,也可以拆出多个函数来。

  • 有效的单元测试

    好的单元测试应该符合几条原则:

    • 简单,只测试一个需求

    • 符合 Given-When-Then 格式

    • 速度快

    • 包含断言

    • 可以重复执行

  • 写刚好的实现

    专注当前需求,不要把其他需求也实现了,不这样会破坏节奏感,实现的时候要小步快走。

  • 重构

    了解什么是Clean Code,要看得出代码的Smell,用合适的「手法」消除 Smell。没有及时重构,等想要重构时已经难以下手了。

  • 基础设施

    对于特定技术栈,把单元测试基础设施搭建好,写测试时就可以专注在测试用例上。

FAQ

  1. 明明知道测试会失败,为什么还要运行一下?

    程序并不只有成功和失败两种情况,成功只有一种,然而,失败有无数多种,运行测试才能保证当前的失败是你期望的失败。

  2. 小步好,但真的要小步到这个程度吗?

    练习过程中应该尽量小步;实际工作中可以先大步,遇到问题再切换为小步

  3. 测试覆盖率多少合适?

    开始学习时尽量追求100%以分辨什么样的测试好写,什么不好写