测试驱动开发(TDD)是极限编程的重要特点,它以不断的测试推动代码的开发,既简化了代码,又保证了软件质量。
1. 优势
TDD的基本思路就是通过测试来推动整个开发的进行。而测试驱动开发技术并不只是单纯的测试工作。
1.很多开发人员最害怕的就是后期还要修改某个类或者函数的接口进行修改或者扩展,为什么会发生这样的事情就是因为这部分代码的使用需求没有很好的描述。测试驱动开发就是通过编写测试用例,先考虑代码的使用需求(包括功能、过程、接口等),而且这个描述是无二义的,可执行验证的。
2.可测试的要求,对代码的内聚性的提高和复用都非常有益。因此测试驱动开发也是一种代码设计的过程。
3.测试驱动开发过程中产生的测试用例代码就是对代码的最好的解释。
4.测试驱动开发提供的测试集就可以作为你对自己工作成果的信心的来源。也是对重构的信心和保证。
5.针对关键代码的测试集,以及不断完善的测试用例,为迅速发现、定位bug提供了条件。
2. 原理
1.在明确要开发某个功能后,首先思考如何对这个功能进行测试,并完成测试代码的编写,然后编写相关的代码满足这些测试用例。然后循环进行添加其他功能,直到完全部功能的开发。
在开发的各个阶段,包括需求分析、概要设计、详细设计、编码过程中都应该考虑相对应的测试工作,完成相关的测试用例的设计、测试方案、测试计划的编写。
相关的测试文档也不一定是非常详细复杂的文档,或者什么形式,但应该养成测试驱动的习惯。
3. 过程
测试驱动开发的基本过程如下:
2 ) 快速 完成针对此功能的测试用例编写。
3 ) 测试代码编译不通过。
4 ) 编写对应的功能代码。
5 ) 测试通过。
6 ) 对代码进行重构,并保证测试通过。
7 ) 循环 完成所有功能的开发。
开发过程中,通常把测试代码和功能代码分开存放
4. 原则
测试隔离。不同代码的测试应该相互隔离。
对一块代码的测试只考虑此代码的测试,不要考虑其实现细节 .
一顶帽子。开发人员完成对应的工作时应该保持注意力集中在当前工作上,而不要过多的考虑其他方面的细节,保证头上只有一顶帽子。避免考虑无关细节过多,无谓地增加复杂度。
测试列表。一是避免疏漏,也避免干扰当前进行的工作。
测试驱动。完成某个功能,某个类,首先编写测试代码,考虑其如何使用、如何测试。然后在对其进行设计、编码。
先写断言。测试代码编写时,应该首先编写对功能代码的判断用的断言语句,然后编写相应的辅助语句。
可测试性。功能代码设计、开发时应该具有较强的可测试性。其实遵循比较好的设计原则的代码都具备较 好的测试性。比如比较高的内聚性,尽量依赖于接口等。
及时重构。无论是功能代码还是测试代码,对结构不合理,重复的代码等情况,在测试通过后,及时进行重构。
小步前进。对于一个类来说,一个功能一个功能的完成,如果太困难就再分解。每个功能的完成就走测试代码-功能代码-测试-重构的循环。通过分解降低整个系统开发的复杂性。这样的效果非常明显。几个小的功能代码完成后,大的功能代码几乎是不用调试就可以通过。你甚至会为这个速度感到震惊。(是大幅度减少调试、出错的时间产生的这种速度感)
5. 测试技术
5.1. 测试范围、粒度
按大师 Kent Benk 的话,对那些你认为应该测试的代码进行测试。就是说,要相信自己的感觉,自己的经验。那些重要的功能、核心的代码就应该重点测试。感到疲劳就应该停下来休息一下。感觉没有必要更详细的测试,就停止本轮测试。
测试驱动开发强调测试并不应该是负担,而应该是帮助我们减轻工作量的方法。
小步前进的原则,要求我们对大的功能块测试时,应该先分拆成更小的功能块进行测试,比如一个类 A 使用了类 B 、 C ,就应该编写到 A 使用 B 、 C 功能的测试代码前,完成对 B 、 C 的测试和开发。那么是不是每个小类或者小函数都应该测试哪?我认为没有必要。你应该运用你的经验,对那些可能出问题的地方重点测试,感觉不可能出问题的地方就等它真正出问题的时候再补测试吧。
5.2. 怎么编写测试用例
测试用例的编写就用上了传统的测试技术 :
操作过程尽量模拟正常使用的过程。
全面的测试用例应该尽量做到分支覆盖,核心代码尽量做到路径覆盖。
测试数据尽量包括:真实数据、边界数据。
测试语句和测试数据应该尽量简单,容易理解。
为了避免对其他代码过多的依赖,可以实现简单的桩函数或桩类( Mock Object )。
如果内部状态非常复杂或者应该判断流程而不是状态,可以通过记录日志字符串的方式进行验证。
6. Tips
防过度设计 。 编写功能代码时应该关注于完成当前功能点,通过测试,使用最简单、直接的方式来编码。过多的考虑后期的扩展,其他功能的添加,无疑增加了过多的复杂性,容 易产生问题。应该等到要添加这些特性时在进行详细的测试驱动开发。到时候,有整套测试用例做基础,通过不断重构很容易添加相关特性。