这里主要想讨论一下单元测试,目前Unit Test越来越被主流开发平台看好,先是Eclipse、NetBeans,现在又是VisualStudio 2005,甚至在Team System中还能施加强制Unit Test的Policy,将来的Weblogic Workshop 9可能也会进行相关集成,业界对此重视程度可见一斑。虽然Unit Test的好处不言而喻,但至今在中国,多数的项目开发仍然不会实施单元测试,即使有实施的,很多也是在外部Policy的压制下被迫进行单元测试的编写,以满足那字面上的测试用例代码覆盖率,真正程序员主动给程序配上单元测试的项目少之又少。其中很重要的一个原因便是多数人认为单元测试极大地浪费了他/她们的开发时间,降低工作效率。其实自从前段时间对XP的实践之后,我能清晰地感觉到Unit Test带来的不仅是软件质量的提升,更是开发效率的提高,这是什么道理呢?看完本文便有答案了。
一个不能忽视的问题是,目前很多软件开发过程中的测试仍然只是随便搞点数据,跑过算数,所谓测试用例的个数毛估估的也是个别现象,我认为这不能算是成功的测试,而且也会为日后项目的进展埋下隐患。说老实话,单元测试并不容易,当然如果我程序的每个类实现的都是加法减法这样的操作,那单元测试或许是相当好写的,可现实世界纷繁复杂的业务逻辑、大量的数据库操作占据了绝对主流,如何进行单元测试呢?
单元测试也有一些原则,首先便是测试先行!无论要实现什么功能,XP的主张:先写测试。在书写测试的过程中,程序员学习并深入了解需要实现的业务逻辑,发现疑问并寻求解答。这是Test Driven Development(TDD)的基本实践,可以避免程序员盲目书写程序代码可能蕴藏的隐患,在没有完全了解或自以为完全了解业务逻辑的情况下写出来的代码很有可能会在将来的某一天被重构或修正掉,与其到那个时候去费工作量,还不如静下心来考虑考虑将要做的程序到底是怎样运作的,有哪些意外情况可能会发生。
其次是无依赖原则,即任何一个单元测试的运行及其结果都不依赖于外部环境,这些环境可能包括操作系统环境变量的配置、数据库中的数据、运行的时间等等。无论何时运行,只要代码不变,结果都应该是一致的。其中接触最多的可能就属数据库了,在单元测试中,开始对代码进行测试之前,首先要准备好测试可能需要的数据,通过把准备数据的代码写入单元测试中,可以达到一劳永逸的效果,一次编写,多次执行,也为自动测试提供了可能。当然设计这些数据往往很费脑筋,要能做出足够量的能测出所有相关业务逻辑的数据并不是很轻松的工作,但也正是在这样的设计过程中,充分考验程序员对需求的认知程度,最大程度避免程序的漏洞和死角。
然后是无踪迹原则,即测试运行前后系统状态保持一致,数据库的数据也不因测试而发生增减,所有测试生成的数据一律删除。这就如同是MGS中的Snake不能留下任何潜入的足迹一样,XP主张单元测试也要不留痕迹,这也是多次自动运行结果一致的重要保障。所以在编写单元测试时,清场的代码千万不可忽视,尤其是那些由正常的业务逻辑代码生成的数据,一定要想办法抓住特征,予以删除。
最后是进化原则,这也是XP的简单教条在测试方面的体现。无论何时都不应期望在一开始就写出面面俱到毫无破绽的测试用例。随着经验的增长,首次测试案例的质量会愈加提升,但在一开始就进行过度测试是不可取的实践。在集成测试中必然会暴露单元测试未能成功捕捉的bug,此时对原先的单元测试进行修改,并通过衰退测试(即用新的测试代码捕捉原有程序的漏洞)验证新的单元测试是有效的。单元测试也和业务代码一样始终处于不断修正、充构、进化的道路上。
说到这里,再来总结一下为什么单元测试提高了开发效率,首先是其一次编写多次自动执行的特征使得准备完善测试数据的工作量得到大大减轻,其次在发生代码重构时,把单元测试全部运行一遍便足以保证重构没有给系统带来破坏性的打击,这种情况下若是没有单元测试,那某些牵一发而动全身的重构或代码修改可能导致的后果就没人能说清楚了,组织人力对所有可能受影响的模块进行人工测试又是另一个庞大的工作量。看似单元测试使程序员多写了很多代码,事实上在很大程度上降低了可能的工作强度,而且使程序员也更为自信,做到面对任何代码修正都心中有底。
当然,单元测试只是测试的一个方面,之后还有界面测试、压力测试等等,我并不想以偏概全,因为自己也曾经吃过光重视单元测试忽视其它测试的苦头,我想说明的是单元测试在目前国内开发实践中的运用还是相当欠缺,而它能解决的问题却是最多的,希望有更多程序员出于自身的需要、发自内心地投入到单元测试的队伍中来。