- 01|TDD演示(1):任务分解法与整体工作流程
- 02|TDD演示(2):识别坏味道与代码重构
- 03|TDD演示(3):按测试策略重组测试
- 04|TDD演示(4):实现对于列表参数的支持
- 05|TDD中的测试(1):状态验证为什么是主要的使用方式?
- 06|TDD中的测试(2):行为验证为什么应该尽量避免使用?
- 07|TDD中的测试(3):集成测试还是单元测试?
- 08|TDD中的驱动(1):驱动的极限是什么?
- 09|TDD中的驱动(2):重构发挥了什么作用?
- 10|TDD中的驱动(3):何为经典学派?何为伦敦学派?
- 11|作为工程化方法的TDD:更低的成本与更高的效能
- 初始化:主要是设置测试上下文,从而使待测系统(System Under Test)处于可测试的状态。例如,对于需要操作数据库的后台系统,测试上下文包含了已经灌注测试数据的测试用数据库,并将其与待测系统连接。
- 执行测试:就是按照测试脚本的描述与待测系统互动。例如,按照功能描述,通过 API 对系统进行相应的操作。
- 验证结果:就是验证待测系统是否处于我们期待的状态中。例如,经过测试,数据库中的业务数据是否发生了期待中的改变。
- 状态验证(State Verification):是指在与待测系统交互后,通过比对测试上下文与待测系统的状态变化,判断待测系统是否满足需求的验证方式。是一种黑盒验证。它将测试上下文与待测系统当作一个整体。是主要的使用方式。
- 增量状态验证(Delta Verification):
- 行为验证(Behavior Verification):行为验证背后的逻辑是,状态的改变是由交互引起的。如果所有的交互都正确,那么就可以推断最终的状态也不会错。是一种白盒验证。旨在降低测试成本,但对 TDD 用处,丧失测试的有效性。
- 复原:就是将测试上下文、待测系统复原回测试之前的状态,或者消除测试对于待测系统的副作用。例如,删除测试数据中的数据(通常是通过事务回滚)
状态验果,行为推因。行为验证是非常有用的测试技术,但它并不合适作为 TDD 的默认验证方式。为了保证 TDD 中的红 / 绿 / 重构循环,我们应该尽量使用状态验证。对于第三方服务和 UI 自动化测试可以使用行为验证。
在 TDD 的语境下,“单元测试”指的是能提供快速反馈的低成本的研发测试(Developer Test)。
- TDD 中的测试是由不同粒度的功能测试构成的;
- 每一个测试都兼具功能验证和错误定位的功效;
- 要从发现问题和定位问题的角度,去思考测试的效用与成本;
- 单元粒度要以独立的功能上下文或变化点为粒度。
TDD 中的测试不是单元测试
从“驱动”的角度来说,TDD 实际上并不是一种编码技术(Coding Technique),它无法帮助实现你不会写的代码,你必须要知道如何实现这些功能;但是一旦你明确了要实现的功能,并且知道要怎么实现,TDD 可以帮助你更好地将功能放置到不同的单元。
- 语义化的查找替换(Semantic Find and Replace)
- 通过提取 / 合并单元进行重架构(Extract and Merge Units)
- 使用多态替换条件
- 经典学派强调功能优先,设计 / 架构后置,通过重构进行演进式设计。
- 而“伦敦学派”并不排斥预先存在的设计,更强调如何通过测试替身,将注意力集中到功能上下文中的某个对象上。然后在测试的驱动下,按部就班地完成功能开发。
如上图所示,使用 TDD 的核心流程为:
- 首先将需求分解为功能点,也就是将需求转化为一系列可验证的里程碑点;
- 如果已经存在架构或架构愿景,则依据架构中定义的组件与交互,将功能点分解为不同的功能上下文;
- 如果尚不存在架构愿景,则可以将功能点作为功能上下文;
- 将功能点按照功能上下文,分解为任务项。也就是进一步将可验证的里程碑点,分解为功能上下文中可验证的任务项;
- 将任务项转化为自动化测试,进入红 / 绿 / 重构循环,驱动功能上下文内的功能实现;
- 如果重构涉及功能上下文的重新划分,即提取 / 合并组件,即视作对于架构的重构与梳理。需调整后续功能点中对于功能上下文以及任务项的划分。
- 如此往复,直到所有功能完成。
TDD 的工程优势: 理解需求,明白架构。
- 第一,理解需求等于可以针对功能点写出测试。
- 第二,不写测试,除了不会写测试之外,就是没理解需求。
- 第三,所有软件从业人士都认为架构是重要的,但却很少有人理解架构究竟是如何发挥作用的。架构是组件职责划分的依据以及组件的交互模式。
- 第四,架构愿景很难在一开始就想得尽善尽美,随着需求发展,总会出现以当前架构愿景不容易实现的需求。