测试是开发中必不可少的工作
首先,一个软件产品或系统的开发成功,不仅仅是编写完为使用者提供服务功能的程序而已。软件程序编写的完成,其实只是完成了开发任务中的一半。与程序的开发相配合的、具有同样重要性的另一半工作,是对开发完毕的软件所进行必要的测试。对测试的管理和执行,其重要性不亚于对程序本身的开发。你可以花费巨大的资源和努力进行程序的开发,可是你要是没有与此配套的完善的测试,所开发出来的软件往往会因为质量问题无法满足客户的要求和帮助你赢得市场的竞争。
近几年来国内信息业界的软件开发的成熟程度大大提高,很多公司都开始重视软件测试的重要性、并建立了与此相关的组织结构来保证测试工作得以执行。但是忽视或轻视测试工作的不良习惯和企业文化仍旧普遍存在。在中国项目管理俱乐部的网站上有业界的同仁们反映了这样的情况:他的公司居然还采用所有的软件开发人员都只做程序编写、只有一个人担任软件测试工作这样一种组织结构,而且这个公司的领导认为只有程序的编写才属于实际的开发工作,因此只知道夸奖程序编写人员的工作成果、完全忽视测试人员的贡献。虽然这样的近于荒唐的例子可能是极少数的极端现象,但在相当大比例的软件企业中测试人员往往仍旧是被当作“二等公民”看待,好像他们只是开发人员的配角而已,对软件最终是否合格和能否发行的判决,并没有实际的影响力。
一个成熟和高效的开发组织应该、也必须采取与此完全相反的做法:将软件的测试和开发放到同等重要的位置上,对软件的测试和开发给予同样程度的重视。这种项目管理的理念就要求对软件测试给予与软件开发相同的资源和支持,用同等的组织结构和人才来保证软件测试得到严格的执行。
微软公司就是用组织结构来保证产品开发的运作流程充分体现对软件测试的尊重、承认测试的重要性。微软总部各个产品部门的所有开发组织都有与程序开发团队并列的测试团队 – 任何开发组织都是由项目管理、软件程序开发、和软件测试三个并列的团队组成。这样的“三驾马车”的组织结构,保证了测试团队是一个独立于程序开发团队之外的机构,软件测试的结果和测试人员的观点在这样的组织结构中不会被程序开发人员随意推翻或践踏,测试人员能够大胆申诉测试结果、坚持测试的判决、包括阻止不合格的软件发行。我在Windows操作系统部门进行视窗嵌入式操作系统的开发工作时,就碰到过好几起因为测试团队坚持测试结果的审判,从而阻止了开发团队能够按时发行开发完毕的软件的情况。
敏捷模式中的测试驱动开发
事实上,现在最新的软件开发项目管理的模式之一甚至将软件测试的优先权提高到高于软件功能本身的开发之上。在敏捷模式(Agile Model)实践范围中的测试驱动开发模式(Test Driven Development,简称TDD)要求将软件的测试机制和可测性首先开发到软件中去,把对软件进行测试的功能作为软件功能开发的不可缺少的一部分来对待。它要求所有软件功能组件都必须有自己的进行自我的单元测试的机能、并且要求程序编写人员在开发软件的功能程序编码之前,先开发进行这些功能自我测试的程序。测试的程序编写完成之后,就开始进行单元测试的运行。这时候,由于提供功能的程序源代码都还没有编写,所以刚开始的时候这样的自我单元测试当然都是通不过的。当这些单元测试都能运行之后,开发团队然后才开始编写每个单元里面的具体功能的程序。
由于进行自我测试的程序已经完成了,每个功能开发完了之后,开发人员就马上可以启动单元的自我测试程序。要是单元的功能程序开发得完善,这时单元的自我测试就能通过,单元的开发就算完成了,测试人员再进一步进入到下一步的其它测试,比如使用案例的测试和系统整合的测试,等等;要是单元的功能程序在开发中有问题或缺陷,这时单元测试就还是无法通过,那么开发人员和测试人员就先集中精力来分析到底是什么缺陷和错误造成单元组件的功能程序无法通过那些自我测试,然后可以根据测试失败的症状进行单元功能程序的纠错工作。
举例来说,如果我们需要开发一个进行文档选择的功能组件。采用传统的开发方法的话,就是开发人员先将文档选择的功能程序开发出来,然后进行黑箱效应测试,证实文档能被选择之后就完了 。采用TDD的开发方法,我们就不是先开发如何选择文档的功能程序,而是先考虑这个组件该如何进行单元测试。所以开发人员先编写在文档选择的使用过程中进行检测可能出现差错的测试程序。这些测试检验代码的白箱效应的测试,比如进行文档选择后的数据检查、以及程序的逻辑检查等等。等到这些单元测试程序完成并能够运行之后,再开始编写实际的文档选择的功能程序。每个局部功能程序编写完毕就立即运行单元测试,直到单元测试全部通过为止。这时开发人员也可以根据软件构架设计的有求,对功能代码中的一些运算方程函数进行模块优化性的分解(也叫Refactor),除去任何重复的代码等等。任何源代码改动和分解之后,必须再次运行所有的单元测试,直到全部通过后才进行证实使用方案的黑箱效应测试,比如检测文档选择正确的结果、选择错误的结果、文档打不开的结果、文档找不到的结果等等。
这样的开发运作流程和管理的理念是,所有的程序都应该有它的可随时启动和利用的测试机制(Test Harness),而且这种测试机制应该是每个软件功能组件单元的不可分割的一个组成部分。我们首先开发这些提供测试机制的程序,建立一个可供测试的框架。然后通过先测试失败、加上功能后然后造成测试成功这样一种反面性的验证,来证实开发出来的软件是符合所设计的测试要求的。所以你可以看得出来,测试驱动开发的模式的主导思想是为满足测试而开发。
比喻来说,这就好像是修建一条铁道线,得先把铁路轨道的标准定了、轨道先铺上,然后再在铁路上运行与轨道宽度标准相符合、专门为它而造的火车。铁路轨道的宽度标准决定了所用的火车必须遵循的宽度。所以可以说,轨道宽度标准是一个制约了火车合格标准的框架。先有了这个框架可以很容易地证实造出来的火车是否符合要求。当然,你也许可以不顾宽度标准先造个火车再说,但这样造出来的火车不见得能在轨道上跑:要是轮距宽度不符合轨道标准,你就得返工重做。TDD的管理模式就是这个先造检测标准的理念在软件开发上的运用,就好像是你先定好轨道的宽度,然后说:按照这个标准造火车,不能在这个轨道上跑的火车就自动不合格;TDD的管理模式使开发人员建立同样的思路:按照这个测试标准去开发程序,通不过这些测试的软件就自动不合格。