是在编写一个方法之前编写它的单元测试,还是在写完这个方法,甚至是整个之后才编写单元测试呢?John Ferguson Smart[1]在他的blog中再次提出了这个问题,并根据自己的经验给出了一些建议。
  都别书生气。在你编写一个方法之前或是之后编写单元测试--根据我的经验,只要你在编写代码的几乎同时考虑并编写单元测试程序,那么这无关紧要了。过后再返回去(或者根本不回去)写测试程序将导致问题。我个人而言,我喜欢在编写少量代码之前或紧接着的之后编写单元测试--这不会打破工作流程,因为它是流程的一部分。
  这需要一点儿实践经验--缺乏经验的开发者经常为要写什么样的测试程序而烦恼。但这可能也反映出一个事实:他们同样也不知道要写什么样的代码。一些人评说TDD能够鼓励进行微设计--一种非常底层的设计,它不需要考虑较大的场景。这会发生在缺乏经验的开发者身上;如果你教条般地应用这种方法,同样也会遇上。像行为驱动开发这样的方法在此处会很酷。当你在写getter方法之前,你会写一个针对这个getter方法的单元测试吗?如果是的话,那么你的单元测试专注的层次较高了,也会更接近于用户(或系统)的需求。
  回到问题的本质,为什么我喜欢把单元测试放在开始的位置?很简单!我的实践经验告诉我,那样可以帮助提高代码的质量,并且节约调试时间。在开始时写十个小的单元测试所花的时间比在以后修复Bug所花的时间要少,如果代码经过了正确的单元测试,那不会有Bug了。
  事实上,我屡屡见到,如果某些代码经过了适当的单元测试,那么不会有编码问题。近有一个例子:花了一个小时的时间去搜寻Web应用中的一个问题,该问题出现在一个编写正确的Spring-MVC程序中。结果是由于一个检验器类忽略了一个异常。很容易发现了这个问题,实际上,在看了代码(代码检查(Code Review)也很有效)之后立刻发现了。但关键是,我们花了一个小时甚至更多的时间去找这个需要进行检查的类。如果这些代码经过了适当的测试,那么能很快地发现并解决这个问题。
  根据我的经验,当人们在编写完程序之后才开始编写单元测试,如同事后才有这样的想法,他们很难写出这些测试了("我已经完成了所有的代码,此时我还得去写单元测试")。或者根本不去做。在这种情况下,代码是否完成了呢?如果代码运行地很好,那算是完成了。这样的话,再写单元测试大大地丧失了它的价值。还不仅如此,事后编写的单元测试将是肤浅的,不会对代码进行良好地测试。或者,开发者已经耗完了时间,他们根本不想再为单元测试伤神了。
  TDD与任何其它的编码实践一样。当你正在学习某个新的技术时,你会倾向于对学习指导亦步亦趋。类似地,当你学习一项武术时,你也会试着一步步地模仿大师的动作,而不必去理解其中的逻辑。一旦你熟悉了某个技术,能够熟练地使用它,并对它有了更深入地理解,然后,你能改进它,并与你之前掌握的其它技术进行溶合了。
  译注
  [1]John是Java Power Tools一书的主作者,也是java.net中一位活跃的Bloger。
  上周在java.net上看到这篇Blog,再联想到自己在平时工作中的单元测试实践,有些感触,故将其翻译了出来,与大家共享。
  事先编写单元测试,还是事后才编写单元测试?这是一个老问题。按照TDD的思想,自然是要先编写单元测试,然后再编写能够通过该单元测试的方法。
  但,单元测试并不是TDD的专属领地,很多不实践TDD的项目也在应用着单元测试。
  我认为,在不实践TDD的项目中(我自己所处的环境是如此),事后编写单元测试仍有着其合理性:
  1.以消极的态度来看,既然项目本身不严格要求事先编写单元测试,那么可以在事后去做了。这至少比不去做要好,聊胜于无嘛。(嘿嘿,是够消极的,但也拿你没办法)
  2.事后编写单元测试至少也是一种检验手段,当然,肯定比不上事先编写的单元测试。因为,事后编写的单元测试很可能会"将"已经写好的应用程序,正如John所说"事后编写的单元测试将是肤浅的,不会对代码进行良好地测试"。但...仍然是聊胜于无嘛:-D(哈哈,有完没完了)
  3.可以把单元测试,其中包含事后单元测试,作为"后来者"了解、学习应用程序的手段。因为单元测试程序是应用程序的"客户",所以无论它是事先写的,还是事后写的,都可以很好地表现出应用程序的行为。
  4.事后单元测试,也可能转化为事先单元测试。在应用程序的整个生命周期中,维护阶段是长的。在"漫长"的维护过程中,"之前"所写的"事后"单元测试将会成为"后来者"(包括原始作者本人)的"事先"单元测试。在改进程序的过程中,这些单元测试仍然能起到监督的作用。(orz,有点儿诡辩)
  虽然,事后单元测试明显不如事先单元测试,但它的作用仍然不可低估。只要编写了的单元测试程序,无论是在哪个阶段,它都会对改进应用程序有莫大的帮助。(这可不是"聊胜于无"能够表达的)