Kevlin Hnney是英国的一位独立顾问和培训师,其关注的范围主要包括软件架构、模式、开发过程和程序设计语言。在本文中他将谈谈如何通过单元测试提高开发效率。
单元测试只会浪费时间吗?某些软件专家们确实是这么想的。最近在Software Quality Insights上看到一篇文章——《单元测试真的有用吗?》。那些认为单元测试无用的开发人员给出了如下理由:
1. 他们不了解单元测试。
2. 很难写出优秀的单元测试。
3. 单元测试只会浪费时间、降低效率。
4. 写单元测试需要太多时间(特别是频繁的迭代开发)。
5. 回归测试更有效率。
本文将重点讨论后面三个问题,即单元测试与开发效率的关系。
效率 vs 人员安排
单元测试会降低效率、造成时间上的浪费吗?这取决于所说的效率是什么,以及所说的时间对象是谁。在一个纯粹编写新代码的周期里,写单元测试的程序员所写代码可能会比不写单元测试的少。如果所说的效率是指这个,那么单元测试确实会降低程序员的效率。
但是,我们很容易发现这种牵强的效率定义的问题所在。代码行并不是衡量效率的标准,它只是所写代码的行数。从单个类到整个系统,我们可以发现很多代码行数已经远超出了实际所需。
我们需要的并不是更多的代码,而是准确的代码。单元测试可以让我们随时进行代码层次上的真实性检查,它可以告诉我们是否在开发正确的东西。所以越多的测试就意味着越少的代码。这并不是什么坏事。开发速率(development velocity)与开发速度(development speed)的区别就在于方向。向正确的方向前进即使几步也要好于在错误的方向上飞奔。
另一个错误的想法是开发人员大部分时间都在编写新代码。虽然开发人员也想专心于代码,但是现实却并非如此。会议(与团队、管理人员、客户、投资商等)、邮件、程序调试、会话、文档制作、安装、研究评估、帮助解决问题、跟进支持、合并版本、处理配置管理系统等都是需要考虑的。
虽然各部分所占比例根据项目与公司的不同而有所变化,但是这些都是与代码无关的活动,而且所占时间总和要高于编写代码占用的时间。问题在于,如果我们把一部分编码时间用于单元测试,那么上面这些时间有多少可以转化为编码时间呢?
局部优化 vs 全局优化
看清全局也很重要。下面Alfred Aho说所的关于开发AWK语言的故事颇有一些值得我们考虑的地方:
“如果再有这样一次机会,我们在开发这种语言的时候肯定会增加严格的测试。我们当时是把AWK当作一种"临时性(throw-away)"语言进行开发,所以并没有考虑严格的质量控制……曾经有个人用AWK编写了一个CAD系统。他来找我,想告诉我AWK编译器的一个Bug。他很生气,说我浪费了他三个星期的生命,因为他用了三个星期查找他代码里的错误,结果却发现是编译器的问题!后来我和Brian Kernighan讨论过这个问题,我们觉得应该做一些质量控制方面的工作。然后,我们针对AWK所有功能做了一次严格的回归测试。从那以后,我们三人无论谁为这种语言增加新功能的时候,都要先写一份相应的测试。”
这个故事的意义就在于它是比较早的拥护“测试先行”编程方式的实例——在实现某个功能之前先把测试写出来,而不是只在计划或者口头说说。另外,这个故事也说明了如何把短期、临时性或针对性代码转变为长期、稳定的方案。
不过对于我们而言,最重要的是帮助证实测试将降低效率这一谬论。这里所说不是代码编写人员的效率——而是用户的效率。或者更广泛地说,是这个开发链下一个环节的人的效率。
所谓测试会“降低效率与浪费时间”,充其量也只是针对编程人员而言。而所有可能出现的问题则会堆积起来,直到下一个环节才被放大,浪费更多的时间和效率。当然,程序员可以继续解决任何后面出现的问题,不过你还会认为这属于“正常工作”范畴内吗?如果人们无法把编写单元测试看作他们正常工作的一部分,他们就可能把它当作是对正常工作的一种巨大干扰,而不是考虑如何在工作中取得一个和谐的平衡点。
解决应该解决的问题
许多局部优化实际上是对全局的掌握不够,因为他们无法意识到更深层次的问题。这样,他们所做的许多解决性措施就只能解决表面问题,而无法触及问题的根本所在。如果迭代过于频繁而无法进行单元测试,这就意味着迭代的频率已经超出了这个团队或机构的产能。这个产能当然包括团队的单元测试能力。
小周期迭代的目的并不是在短时间内进行迭代,迭代的目标是减少风险,以及通过持续发布功能性完整的增量版本来提高价值、改善工作流程。如果有达不到要求的地方,那么很可能是系统中的某一部分限制了迭代频率。如果无法解除这个限制,那么最好还是延长迭代的周期,使其更适合当前的环境,而不要“赌气”似的强制使用这个周期。
被动式的回归测试
为什么有人说回归测试比单元测试更有效呢?虽然具体还要取决于回归测试指的是什么,不过这也暗示了他们对回归测试作用的误解。
通过再运行一次同样的测试,回归测试保证了对代码所做的任何改动并没有影响到先前测试过的行为。但是新功能呢?按照定义,你不能用回归测试确定新功能的行为或者新功能可以实现预期的行为。所以关键就是要明白“回归”这个词。
回归测试并不是一种仅仅与单元测试不同粒度的测试,它表示的是一种预期的测试结果。回归测试可以在单元、系统或者之间的任何层次上进行。它的意义在于保证不应改变的部分没有被改变。而新测试则是要保证发生改变的部分已经根据预期产生了相应的效果。
文章来源于领测软件测试网 https://www.ltesting.net/