1.4 TDD的微循环
我将从什么不是TDD说起。它不是花一个小时、一天或者一个星期来写一大堆的测试代码,然后再来实现产品代码的开发方式。
TDD是写一个小小的测试,然后写仅够让这一个测试通过的产品代码,同时不能破坏已有的测试。
TDD要求你在构建一个东西之前先决定你到底要什么。它将反馈给你所有的东西是否都和你当前的预期一样。
TDD的核心是由小步骤来不断重复的循环组成,称为TDD微循环。新的和老的代码是否还保持我们期望的行为?每次循环都会给出这个问题的答案。能得到这种反馈的感觉很棒。进度也很实在,而且这种进度也是更容易度量的。错误变得很明显。
以下的TDD循环步骤基于Kent Beck在其所著的《Test-Driven Development》[Bec02]一书中的描述:
1)增加一个小的测试。
2)运行所有的测试并期待新测试失败,也可能根本连编译都通不过。
3)为了让测试通过来做一些小改动。
4)运行所有的测试并期待新的测试通过。
5)重构,以移除重复并改进代码的表达方式。
TDD循环中的每一步都只需要几秒到几分钟的时间。新的代码和测试以增量的方式加入进来,并且立刻得到关于我们新写的代码是否和我们预期一致的反馈。你把脑海中预见到的愿景“生长”成代码,从简单到复杂。
在这个过程中,你不仅了解到了解决方案,同时也对所解决的问题有了进一步了解。测试变成了对需求细节的翔实描述。当工作不断地累加后,测试同产品代码一起绑定了对问题的定义和解决方案。这些知识以稳定的形式被捕捉下来。
不论进行何种改动,都要运行一下测试。如果测试通过,那么它会告诉你一切正常;如果产生了意料之外的结果,它会给你警告。形象一点来讲,这意味着如果你把代码搞坏,那么它会尖叫!
如果测试已经通过,这感觉当然不错。因为这是一个坚实的进步。但是软件工作该做的事情还不止于此。
保持代码整洁和富有表现力
通过测试表明了正确的行为。代码还必须可以运行。但是软件还有更多的错误行为。代码应该保持整洁和良好的结构,以体现我们专业的素养并为将来能轻松地改动投资。整理代码有个专用的名字,它就是这个不断重复循环中每次的最后一步——重构。Martin Fowler在他所著的《Refactoring:Improving the Design of Existing Code》[FBB+99]一书中是这样描述的:重构就是在不改变当前外部行为的条件下对现有代码结构进行修改的过程。其目的是通过写易于理解、易于演化并且易于我们自己和他人维护的代码来让工作变简单。
小的乱代码很容易写出来。遗憾的是,它们也容易被忽略。这些乱代码越到开发的后期越难清理—是你创造了它。在这些乱代码还新鲜的时候就要清理。“测试全过”给了我们做重构的一个好机会。贯穿本书都会讨论和演示重构,尤其是在第12章中。
TDD首先会帮助代码工作起来,但更大的好处还在后面,在后来的开发者理解和维护这些代码的时候。那时就可以(几乎)毫无畏惧地修改这些代码了。
测试代码和TDD首先支持代码的作者,以便让代码正常工作。当我们面向未来时,它更加是和代码的读者相关的,因为测试把我们正在创造的是什么东西描述给读者。
你可能听做过TDD的人称这种节奏为“红灯-绿灯-重构”微循环。