在学校读书的时候,我有很多与我不同专业的朋友,建筑的,桥梁的,机械的,等等。他们有一个与我不同的共同之处,都常背一块大木板,机械制图是他们很重要的课程。我和我的同学们则学习程序设计,学习计算机的结构和原理。我们往往抱怨操作系统编译原理太复杂,可是看看那老大一张纸上铅笔细细勾出的房屋结构机械零件,精确到0.1毫米的内径外径,钢筋水泥混凝土的组成结构及抗这抗那的能力,我觉得简单考量一下的话,二者本并不具直接可比性的复杂程度至少是在一个量级上的。
我也知道一些各行业的工程师,包括我的姑姑是桥梁设计师,我的父亲是机械模具设计师。从小我就对父亲那一卷卷的图纸印象很深。父亲从无到有在一张张白纸上勾出一幅平面的在我看来乱七八糟什么也不是的东西,可是按照它对原料裁剪、加工就变成了一个实实在在的产品。当时觉得神奇,现在想来,这是需要很扎实的知识的。在设计图纸的整个过程中,并没有什么工具和方法可以检查一下是否有错误或疏漏,而最终送到工人手里的图纸必须是正确无误的,否则原料就成了废品。
作为一个工程师,确保所从事的工作是正确的,对于工程师们是很重要的。假如建筑师因为偷懒疏忽而不能使我们的房子十分结实,将会发生什么情况?房子会倒塌而且我们要受到伤害。假设GM的工程师们对于汽车刹车不做最终的测试,当我们需要刹车时,它就可能不能正常工作,就可能出事故。所以当工程师回答一个有关如何工作的问题时,必须确信自己是正确的,必须确信没有忘掉什么。
要做到这些,是需要大量工作的。
而软件行业好象有着很大的不同。也是还在读书的时候,我就曾问自己,同样是工程师,为什么软件行业的工程师不能像传统行业的工程师一样对自己的工作的品质有着如此的确信?
在很多方面,程序设计师还是有着相当的便利的。譬如,在从开始编写代码直到完成最终的软件成品的过程中,每当完成一个功能、一个模块、一个代码段,或者干脆程序员对自己不自信的时候,都可以运用各种工具编译、跟踪、调试程序去发现隐藏的错误或疏漏。而即便是由于偷懒疏忽没有发现错误导致最终的产品中有很多的bug,似乎也不会发生什么,市场仍然接受,用户仍然使用。
有两个数据可以说明程序设计师的工作品质:人们发现,即使具有较多经验的编程人员,其编程正确率的得分平均只有7.8/14.在有经验的编程人员写的代码中,平均每150行就会有一个bug.是什么导致了这样的情况?
是程序员心浮气躁,责任心不强?是软件行业的复杂程度远远超过传统行业?是行业的特殊性造成市场和用户对如此高的错误率持接受态度?还是其他的什么原因?
给自己提了这么些问题,却不知道该怎么回答了。
对于第一个问题,这确实是大量程序员的写照。从这里产生的大量问题也确实严重影响了软件产品的质量。
对于第二个问题,我想起了一个经典的对话:程序设计行家说:“任何程序,无论多么小,都有错误。”
新手不相信行家的话。“如果一个程序小到只能执行一个单一的功能,也是这样吗?”他问道。
“这样的程序不会有任何意义。”行家说。“假如这样的程序存在,操作系统最终也会由于一个错误而失效。”
新手并不满意。“如果操作系统不失效呢?”他问道。
“没有不失效的操作系统。”行家说。“假如这样的操作系统存在,硬件最终也会由于错误而失效。”
新手仍然感到不满意。“如果硬件不失效呢?”他问道。
行家长叹一口气。“不存在不失效的硬件。”他说。“假如存在这样的硬件,用户还会要程序做不同的事情。这也是一个错误。”
没有错误的程序是荒唐可笑的,是不可能存在的。假如存在没有任何错误的程序,那么世界也会不复存在。
这个故事给不负责任的程序员以借口。但事实是否真的如此严重?对于硬件来说,高密集度的电子元件集成使人们很容易理解它是不稳定的这样一个结论,但从现实情况来看,硬件的稳定性要远高于软件的稳定性。
或许,软件规模的不断膨胀使其复杂度指数增长,个人能力已无法完成,团队成为必须。团队磨合也成了产生错误的隐患。但在传统行业里,这种情况也是屡见不鲜的。扬浦大桥,东方明珠,金贸大厦也不是一个人完成设计的。是缺乏经验没有磨合团队的良好方法?
软件设计究竟有多复杂?是不是已经复杂到了不可能避免错误的程度?我想这不是一个很容易可以回答的问题。
对于第三个问题,我认为也是一个主要的因素。虽然每个公司每个程序员都知道,减少错误可以博得用户的青睐,战胜竞争对手。但普遍来说,市场的认同纵容了公司和程序员不负责的心态,毕竟,减少错误是要付出代价的。
在很多公司和程序员看来,bug和测试似乎是紧密关联而分不开的。程序员只顾编写代码,认为有没有bug有多少bug测测就知道了。
把软件质量依赖于测试,是不可能真正解决软件质量问题的。
测试不是解决错误的根本举措,只是一种辅助手段。但又是必须的手段,我想现在已经不会有人再问出“如果程序员更仔细一点,测试会是不需要的吗?”这样的问题了吧。
软件测试的首要任务是发现错误。发现错误也许要花很大的代价。因为测试是复杂的,不存在好的办法使每次测试都是有效的。有这么一句话:如果做某件事有两种或多种方法,其中有一种方法会导致一个灾难结局,那么也会有另一种方法导致同样的结局。
测试的复杂性和软件的复杂性是一致的。也就是说由于软件的复杂导致了测试的复杂。
测试提出了基本的和令人困惑的难题。假使我们在测试时从来不希望检测被测系统所有可能的输入、路径和状态,那么应该选择什么?什么时候应该停止?如果我们必须依赖于测试来防止某种失败,那么我们怎么来设计既是可测试的又是有效的系统呢?我们怎样来编写一个测试包,它可以检测足够多的消息和状态的组合来说明没有失败的操作,但是从实用性来说它又足够的小?
第二个目的是对于给定的测试包,说明被测系统是符合规约所描述的需求。
所以,我觉得测试活动与软件过程协调一致得好与坏,都对测试的有效性有很重要的影响。
如果测试的开发是在一个项目开始时进行的,那么测试将是非常有效的。及早考虑可测试性,及早进行测试设计,和尽可能早地实现测试,提高了有力预防错误的方法。这些过程迫使设计人员更仔细地考虑需求和规约的实现,其结果可能改进应用设计。关于体系结构、详细设计和编码实践的及早决策,都能使测试变得更容易更经济。
写到这里,我想应该停下来了,虽然我还有一些问题和想法,但无法固定它们,我觉得已经有些糊涂了。最后,从脑海里跳出了一个问题:测试与对质量的承诺是一致的吗?