良好的单元测试策略给我们增强了对程序的信心,减少了bug的产生及bug的潜伏期。降低修改bug的代价。单元测试不会是项目开发周期的某一个生命周期,它贯穿于项目的整个生命周期,是一个非常重要的日常开发活动。
 
  我们已经知道了单元测试是多么重要的。为什么程序员仍然不编写单元测试呢?为什么程序员总是有理由拒绝编写单元测试呢?

  一、编写单元测试,增加了工作负担,会延缓项目进度?

  这是笔者在多次讨论和调查中程序员拒绝编写单元测试的多理由。“为了完成编码任务,没有足够的时间编写单元测试。编写单元测试会导致不能按时完成编码任务,推迟项目进度”。事实上真的是这样的吗?软件有着其特殊的生命周期,软件开发也具有特殊性。

  首先,我们需要提供给用户的至少是一个能运行的产品。不能是一堆不能运行的和充满了异味的哑代码。只有能够运行的,满足客户需求的代码才是真正有用的代码。这时代码变成产品了。

  很多程序员只关注编写代码的完成时间,而乎略了调试代码,集成及修改和维护时间。

  如果没有单元测试,开发活动会是这样的情景。

  以一个web应用开发为例:业务代码编写完成->打包->发布到服务器->进行功能测试->发现问题->修改代码->再打包……如此循环。

  任何一个web程序员对于这种开发情景都不会感到陌生。往往不断的打包,发布,功能测试的时间是代码编写的10倍以上。通过集成系统来发现程序的bug,我们往往很难一下子准确的定位bug产生的地方。应用服务器提供的错误信息对于我们来说是非常有限的。

  如果为每一个类都编写单元测试并让每一个方法测试通过,又会是怎么样的开发情景呢?

  编写测试代码->编写业务代码->运行测试方法->修改代码让测试通过->所有的类都通过测试->打包->发布到服务器->进行功能测试->发现bug->修改测试代码->修改业务代码->测试通过->再打包…如此循环。

  从上面的过程显而易见,我们需要花费更多的编码时间。因为需要为每一个业务类编写测试代码。但是它并不会导致我们总体需要花费更多的时间。我们 只是可以非常轻松的在ide环境中运行测试方法。在代码尚未打包发布之前我们已经确保了业务代码的正确性。当我们把所有通过测试的代码集成到应用服务器 后,出现错误的机率要少得多。当集成测试后发现bug时,我们也总是先修改测试类。保证在集成之前所有的类都经过测试通过。这样功能测试的时间成数量级 的减少,所以总的花费时间要比没有单元测试要少得多。

  另外,如果没有单元测试,会经常出现一些低级的错误,如拼写错误,空指针异常等。因为一个小小的拼写错误而需要重新打包,发布一次。如果有单元测试,可以避免这些低级的错误。

  如果没有单元测试,把代码集成到应用服务器后再发现错误时,我们往往更多的是凭借自己的经验来判断问题出在哪里。对于没有经验的程序员来说只能是撞运气了。这像是瞎子走路一样,两眼一把黑。如果每个类都有单元测试,无需要这么痛苦了。

  这使得我回想起当年做网络系统。当时的局域网络都是采用环状网络,还没有现在的交换机来组星形网络。环状网络的传输网络采用同轴细缆线,网络中的所有节点都在一条主干线上,网络的两端都会加上一个电阻来形成一个环。

  环状网络的大的缺点是当任意一个节点有固障时,整个网络都不能连通。维护这种网络是非常麻烦的。通常采用得比较多的方法是“切香肠”法。 把后一个电阻取下来,接到第二台电脑的网络节点的末端,检查两条线是否能连通。连通后再把电阻取下来到第三台电脑的网络节点的末端,连上第三台电脑。这 样来依次检查整个网络的线路。

  后来发展了星形网络,也是现在局域网普遍采用的。有一台交换机,每一台电脑连接到交换机,任意一个节点网络故障不会影响到其它节点,检查起来非常方便了。没有单元测试的代码像是环状网络,而有测试的代码像星形网络。

  其次,有可能我们第一次编写的代码是没有问题的,但是到后来需求改变而修改了其中某些类的代码,把它发布到了应用 服务器去测试,所要修改的内容已经测试通过了。但是因为某些类的修改导致了其它类不能正常的工作。这种bug往往隐藏得非常深,因为只要不触动它,它不 会出现。可能会程序发布到生产环境之后才会被业务人员发现。如果每个类都有测试代码,我们在打包之前运行所有测试代码,可以很容易的发现因为代码修改带 来的连带性错误。

  其三,在离bug产生越近,修正bug越容易;在bug产生越远,修正bug的代价越昂贵。假设我们去集成一个星期(甚至更长时间)前编写的代码,当发现问题时,我们已经忘掉了很多重要的实现细节,所以修改变得困难重重。

  编写单元测试,并不会加重程序员的负担,反而提高了程序员对程序的信心,大大的减少了重复打包,发布,纠错误的时间。这些花费的时间远远要比编写单元测试花费的时间多几个数量级。编写单元测试,可以让你更容易和更放心的去修改代码,增加功能从而加快了项目的开发进度。

  为什么我们总是要主观的去认为编写单元测试会延缓项目进度呢?与其痛苦的挣扎,还不如去尝试一下好的实践。
 
  二、业务逻辑简单,不值得编写单元测试

  程序员是聪明的,程序员也总是自认为是聪明的。认为一些业务逻辑比较简单的类不必要编写单元测试。我们必须承 认,需求不断变化,我们也必须要有勇气去接受需求变化。编写单元测试的另一个目的是拥抱变化,而不是拒绝变化。编写单元测试是提高了我们对程序的信 心。在敏捷软件开发中,代码为集体所有,项目组的任何一个人都可以去修改任何一个代码文件。每当我要去修改一个别人编写的代码时,我总是多么的希望有程序 的单元测试代码,而往往都让我非常的失望。一般我都得花费很大的力气去猜想原作者的原始意图。也许你会说:“你可以去看需求文档啊!你不会去看注释 吗?”。但实际情况是,当需求文档完成了它的使命后,开发人员把它扔到了一边了,文档总是过期的。没有几个项目组能够使得需求,设计这些文档与新实现 代码保持一致。所以去看一个过期的文档是没有价值的。注释也同样,保持新仍然是一个大的问题,并且注释能够提供的信息是非常有限的。所以我需要的 是看测试代码了。测试代码能反映出方法新的功能契约。由代码的编写者去写的单元测试要比由其它人去编写的单元测试要更完善,更准确。