TDD 认识进阶1
这段时间整理了一下TDD的一些认识:
TDD路线
需求(不清晰、不完整)-》设计(不清楚、不完整)-》测试(需求清晰化、设计清晰化)-》实现-》重构
不是一开始就写测试,而是一开始分析需求,这个需求往往不清晰也不完整。针对这个需求我们要做大体的设计。然后开始针对某个需求编写测试用例,写测试用例的时候你会发现隐藏在背后的需求,这也是需求清晰化的过程。这样开发人员一开始就可以接触需求,而不是一开始搭框架。针对测试用例编写实现代码,然后适当重构。再继续针对下个需求编写测试,如此往复。这样不但紧紧的抓住需求,保证你的代码都的可测性,最后还留下监护代码质量的测试工程。
这就好比造飞机
按照当前我们的软件开发方式。我们先分析我们飞机的指标(需求分析),我们画出飞机的样图(系统设计),然后针对样图开发或购买我们要的部件,并设计部件之间的接口(架构设计)。接下去分配模块给各个工程师(模块划分),比如我开始造推进器(开发dao、service等),造好以后我们进行模块测试(编写测试用例),然后我们开发另外的组件比如机翼。如此往复,直到整个飞机的组件都组装完成(开发完成)。开始试飞,你规定了几种试飞规则,比如起飞、降落、平行(系统验收)。最后你就可以交付了(完成开发)。
那么正规的飞机制造时什么样的呢?我们分析我们飞机的指标(需求分析),画出飞机的样图(系统设计),然后针对样图开发和购买我们要的部件,并设计部件之间的接口(架构设计)。接下去分配模块给
各个工程师(模块划分),比如我开始造推进器(开发dao、service等),我们不是马上开始造,而是先罗列推进器指标,比如马力、转速、能耗等,然后根据各个指标进行需求分解,比如针对能耗的指标我们需要开发多少厘米的管子,那么多少厘米的管子就不在原始的指标里,这个是隐藏的需求,这样做其实就是把我们要什么样的推进器的需求细化,细化到可以那我们的尺子、气温计、压力计等最普通的检测工具来测试为止,一直到最后就有了这个推进器的非常详细的需求说明书(测试用例),根据我们的需求说明书,我们就可以开发出符合原始指标的推进器,以此类推,我们一一的按照各自的指标完成各个部件,一旦哪个部件需要更换零件,就只需要找到那个零件的指标说明书,就可以方便的测试这个新零件是否也满足需求。现在开始试飞,你规定了几种试飞规则,比如起飞、降落、平行(系统验收)。最后你就可以交付了(完成开发)
根据以上的对比,我们的开发和造飞机非常的类似,但是也有出入。因为硬件的原因,飞机的部件在开发前必须有非常详细的需求说明书,而软件开发却可以边写测试用例,边细化需求,边开发实现,这个方式要优于硬件开发模式,因为利用编译器的错误检查来时刻驱动我们实现代码并去掉错误,那是一个提升自我信心的好方法。测试写完了,也意味着实现也写完了。边理解需求,分析需求,并实现需求,而不需要等全部的需求整理完成再回过头来实现需求要好的多,因为人的记忆力是有限的,而且人都不希望重复着的做同一件事情。其实最重要的是在你实现了一部分代码后,你才能挖掘更深入的需求,并时刻重构你的代码,让你的代码保持最优状态。
那么我们具体要怎么做呢:
设计的时候要做功能分解、从使用的角度出发做简单的接口设计,避免需求太过泛化,如果你的需求需要很多代码来实现,那么要对需求再细化,一般只需要满足当前的需求设计就够了,没必要考虑的太长久。
对你认为应该测试,或者自己没有信心的代码进行测试,其中包含重要的功能和核心代码,测试不应该是负担、应能减轻工作量,如果你觉得写测试浪费时间,大可自信的去写实现,如果你反悔了,也可以补上测试代码。
编写测试的时候需要做到细粒度测试、保证实现的可测性、保证实现的高内聚、保证测试代码和实现代码的复用。一般都需要在心里有一定的测试列表,然后编写测试代码,用断言来突出实现的需求,依赖于接口,并且适当使用IOC和MOCK就可以方便的实现单元测试,而不需要因为依赖太多的组件,而延迟测试
用测试来推动实现,实现完后要立即重构。
上面这一步可以认为是在测试的时候做设计。一定要带着设计的思路去写测试用例,不然就会脱离架构,为了测试而测试,导致实现代码没有层次和灵魂。
做到上面一步的好处体现在下面几点:
测试用例可以解释实现需求的过程,测试代码就相当于文档,看了测试代码就可以不看文档也能明白
让开发者对每一步的开发都具备信心,每次关注的都是当前的测试
因为每一步都是正确的,并且每一步都需要保证前面的测试时可运行的,所以整个代码都得到了保障
如果一旦出现问题,不需要做整合测试,单元测试就可以发现问题。