高效开发与彻底测试
2010-09-04 20:48:16 来源:WEB开发网三、实现彻底单元测试
是什么使单元测试难于实施?首先是代码的可测性。可测性是什么?如果一个类具有基本的可测性,那么把它加入到另一个工程后(当然有关联的文件也要加入)能够通过编译,这其实是很低的要求,但对于一个有一定规模的项目,如果开发调试时使用自然驱动,在完成编码后才进行单元测试,那么通常都不具有可测性,因为开发人员常常在无意之中使代码之间产生了不当耦合,这些不当耦合累积起来,会使整个项目的代码纠缠在一起,造成难于测试。
使单元测试难于实施的另一个方面是建立测试用例。在本例中,如果由不熟悉代码的测试人员建立测试用例,那么他很可能不知道如何生成CToken对象 指针的列表。
如果边开发边使用专门驱动(测试代码放在另一个工程中)或自动驱动调试,那么一旦出现影响可测性的不当耦合就会及时发现及时解决,保证了代码的可测性,另一方面,由于至少建立了一个测试用例,测试人员建立其他用例时只要修改一下输入输出数据,从而大大降低建立测试用例的难度。总之,使用专门驱动或自动驱动调试,在不增加开发工作量的同时,已经为单元测试打一下了坚实的基础。
范例项目V0.1处于这样一个阶段:刚刚完成代码编写,并未完成单元测试,现有的多数测试用例都是编码时用于调试的。在此基础上,单元测试由谁做,难度都不大。VU的典型应用是通过三个 阶段来完成针对一个函数的彻底的测试:
1)基本功能测试:测试代码的基本功能;
2)完成白盒覆盖:在现有用例的基础上,使用测试用例设计器为未覆盖的语句、条件、分支、路径设计测试用例,达到100%语句、条件、分支、路径覆盖;
3)执行自动边界测试捕捉意料之外的错误。
以上三阶段可以由不同人员在不同时间完成,团队可以根据实际情况做出灵活安排,下面是一个典型的开发测试流程。
1)开发人员边开发边使用VU调试测试,完成基本功能测试。在VU的支持下开发调试,可以大幅提高开发效率,绝不会影响开发进度。也许读者会认为,开发人员没有时间去设计测试用例,其实这是一种误解,开发人员写代码时肯定要想清楚代码的功能并且要使用基本的输入进行调试,这些就是基本的测试用例,实际上不需要多做什么。开发人员提交代码同时提交测试代码和测试报告。如果项目已完成或部分完成编码,也建议先由程序员首先对重要代码进行基本的功能测试。
2)测试人员检查基本测试用例是否符合设计,并在此基础上完成白盒覆盖和边界测试。由于有了初步的测试,保证了代码可测性,不可能产生因为不当耦合造成难于测试的状况;在现有用例的基础上,使用测试用例设计器找出遗漏用例也不会有太大障碍,这就使测试人员的工作易于进行。测试人员只需要提交更新过的测试代码和测试报告,不需要另外记录测出的问题。
3)开发人员下载新的测试代码和测试报告,执行整体测试,然后针对报告了错误的函数执行函数测试以获取详细信息,必要时进行调试,找出错误,修改代码,使所有测试通过,再次提交产品代码和测试报告。
4)测试人员再次执行整体测试,验证所有的测试均已通过。
以上流程是动态和反复的,并不是编码完成后再单元测试。开发人员写完一个类后即可提交代码由测试人员完成白盒覆盖和边界测试。另外,团队可以根据开发与测试人员的比例调整工作份额,如果团队没有测试人员,那么由开发人员完成全部单元测试也是可行的。
彻底的单元测试对于软件开发来说,其价值是难于估量的:除了保证局部代码的质量外,有了单元测试,任何时候修改代码后都可以通过回归测试来自动检查修改是否引入错误,这就使开发过程可以适应频繁变化的需求,系统分析、概要设计和详细设计都可以做得简单一些,也更能适应螺旋式开发过程,以后的维护升级成本也会大幅降低,同时,高质量的代码使集成测试和系统测试的工作量降低很多(实际上单元测试已包含了大部分的集成测试),发现问题后的修改也会高效得多。总之,要提高软件开发质量、降低软件开发成本,最有效的改进就是进行彻底的单元测试,如果不进行单元测试,任何流程改进都无法保证产品质量,因为,程序始终是由代码构成的,代码的质量没有保证,软件的质量拿什么来保证?单元测试并不排斥其他过程改进,相反,单元测试对开发流程中的所有环节几乎都有促进作用。
下边再谈谈关于白盒覆盖的话题。使用VU实施单元测试,100%的语句、条件、分支覆盖通常都是很容易的,路径覆盖有时候会很难,例如,我们所举的例了,CExFunction::ParseOneParameter (),有九十多条路径,要覆盖似乎不现实或没必要,这种状况通常是设计不合理造成的,例如,CExFunction::ParseOneParameter ()函数的代码分为三块:1、解析缺省值,2、解析数组,3、解析类型和参数名,前两块解析了缺省值和数组后把相应的Token从列表中删除,这样的话,第3块与前两块是没有逻辑关系的,但是,这3块代码会组合出很多路径,没有逻辑关系的代码所组合出来的路径是没有意义的,这些代码具有"高耦合低内聚"的特征,不应该放在一个函数中。范例中另外写了一个函数:CExFunction::ParseOneParameter2(),把以上三块代码分别独立出来自成一个函数,这样 每个函数都能完成100%的路径覆盖,重构后的代码既易于测试,也易于维护。范例中有大量类似的函数,甚至有超过200行的函数,这是为了检验VU的适应能力,以后的版本是会进行重构。我们建议程序员完成编码后,检查一下路径数量,如果路径很多,代码很可能需要分拆,合理的路径数量应该是等价类数量的一至两倍。另外,从逻辑结构图也可以看出来:图中有两个或两个以上串联的复杂分支结构,往往表示代码的结构有问题。
VU的逻辑结构图具有屏蔽对象的功能,因此,遇到上述情形时可以通过交替屏蔽部分分支结构的方式来实现路径覆盖,但这不是我们推荐的方式,因为它虽然可以保证测试的完整性,但并没有改进代码的结构。
关于单元测试和范例项目,还有很多值得叙述的话题,例如内存泄漏测试以及一些复杂问题的处理等等,这里暂且不谈。最后谈谈已完成编码的项目的单元测试。对于已完成编码的项目,最好先由开发人员"去耦合",方法是将代码文件从底层向上排列,按顺序依次将文件加入到另一个工程并编译,如果产生编译错误,则想办法消除编译错误 ,VU提供了文件排序工具,具体的使用方法请查阅帮助中《测试旧工程》部分。 完成“去耦合”后,由开发人员对自己编写的代码完成基本功能测试,测试人员完成白盒覆盖和边界测试。对于编码过程中使用自然驱动调试的已完成编写的代码,完全由测试部门进行单元测试通常是很难的。
更多精彩
赞助商链接