当我运行测试时,我失败了,预期的结果为 31,但实际结果为 32。怎么了?唔,我知道问题一定出在刚才所写的代码中,没有进一步考虑下去。在检查完 else 子句之后,我明白我只是根据年来计算年龄。这不对。我现在 31 岁,但这个月再过几天我要 32 岁了(但我写该代码时,是 3 月份),我的算法造成错误的结果。所以需要重新考虑 getAge()。我用清单 6 中的代码段纠正了这个错误:
清单 6. 改正后的 getAge()

绿条!在 Person 类中有一些重复的代码,但我把它留给稍后的重构练习。欢迎替我清理该代码。您可以有信心地做这件事,因为可以运行测试来证实您没有破坏任何事物。
这个示例使您体会到了测试驱动的编程类似于什么。我只在每步编写足够让测试通过的代码。作为一种理论,这在思想倾向上是一种挑战。您必须习惯这种思想,在编写代码之前,可以并应该编写测试。在通过所有测试之后,就完成了工作。
在先编写测试时,必须习惯故意只看眼前。清单 6 中的示例是一个十分简单的情形。即使最简单的编程问题,在实际当中通常要更复杂。这种方法有助于将问题分解成更可管理的部分,但您最终仍可能遇到一些复杂的令人头疼的问题。在那些情况下,必须使自己不要考虑太远,不要假定它的“普适性”有多高,也不要假定这种方法能处理某些尚未遇到的情形。仅仅编写测试,使它通过。您需要采取一些较小的步骤,然后编写迫使您要采取更多步骤的测试。请记住,您正在测试代码的存在性,如果您以较小的步骤来编写代码,那您就做对了。
为什么应该先编写测试……
也许您不认为先编写测试是一个好主意。它看上去似乎很奇怪,或者也许似乎没有必要。我常从富有经验的程序员那听到第二种原因。这些程序员很聪明,他们具有许多经验,他们说不需要先编写测试,因为他们知道自己在做什么。我能领会,但我怀疑他们存在一个隐式的假定:他们没能领会先编写测试。恕我难以苟同。事实上,我认为采用先编写测试方法有三个原因:
学习
新问题
信心
先编写测试 — 到后面再执行这些测试 — 是较佳的学习方式。它使您能将精力集中在所编写代码的接口部分。在编写测试时,您假设正在使用的类已经存在,然后按照您希望在系统其余部分中使用的方式来使用该类。稍后,当您忘记如何使用该类时,可以查看测试,看一个非常具体的示例。这是学习的很好方式。
关于先编写测试最有趣的事情之一是,它有助于发现新问题。您正在使创建之中的系统“成长”起来。如果您正在使用 XP,则没有预先设计整个事情 — 而是一边开发一边设计。在您先编写测试并通过测试这个过程中,您正在让代码告诉您它想要做什么,以及会成为什么。如果仅仅着手编码工作,则您完全按照您的设想来行事了。越晚做决定,则越有可能发现新问题和新动向,这些可使您的系统更完善。
但是,我所喜欢的先编写测试的好处是让这些测试在稍后执行。在我先编写测试时,我有许多奇异的逻辑。我可能没有涵盖代码的方方面面,但会包括其中许多方面。在任何情况下,我会有一套测试,这套测试比我曾参与过的大多数没采用 XP 的项目要更好。我可以按一个按钮就运行这些测试。几秒种之后,我就知道代码是否按我告诉它应该怎样的方式来运行。这种可回归的工具是很有价值的。我团队中的任何人(或任何地方的任何人)可以在任何时候更改代码,甚至在代码发布的前一天也可以更改,因为如果有任何问题,测试会立即告诉他们。作为一名程序员,这给予了我信心 — 比大多数程序员具有更大的信心。
帮助形成“揭开极端编程的神秘面纱”的未来
一如既往,我热忱邀请您就以后的专栏文章提出您的反馈意见,这样有助于促进这个专栏。关于 XP,您存在的最大问题是什么?您认为是完全愚蠢的、不明智的、非专业的还是不可能的?最让您感到迷惑的做法是什么?请在本文的论坛提出您的建议,或者就直接给我发电子邮件。