下面详细说明,C++单元测试不支持reflection,所以,必须要做一些额外的工作,让框架知道相关内容的存在。CppUnit的做法是用宏进行注册。这种做法要求我们每添加一个测试,就要考虑用相应的宏进行注册。
在这点上,CxxTest做得要好一些,有一个专门的脚本做这件事。通过这个脚本扫描这个自己编写的文件,生成一些新的文件,完成这个工作。从代码的表现力和可靠度来说,要好得多。
唯一的问题是引入了一个脚本,而且这个脚本一般是由某些动态语言写成的(目前的CxxTest有Perl和Python的脚本),从而引入了对这种语言的依赖。不过,由于C++语言本身的限制,从接口的角度来看,这种做法已经很不错了。
语法
有一种C++单元测试框架叫TUT,Template Unit Test的缩写。顾名思义,它是用模板完成的(其实,CppUnit和CxxTest都有模板的部分)。随着C++编译器的进步,在大多数情况下,模板都是可以顺利通过编译的。
但是,不要忘了,还有一种环境叫嵌入式,那里的编译器基本上还是很原始的,模板并不见得能够顺利的通过编译。此外,模板还会带来另外一个问题,编译时间的增长,相信有过模板编程经验的人都会对此深有体会。编译时间增长意味着什么?我们接下来讨论。#t#
编译时间
有一种敏捷实践叫做测试驱动开发(Test Driven Development)。测试驱动开发的基础是单元测试。测试驱动开发希望达成的一个目标是快速反馈,所以,站在C++语言的角度,如果执行时间受限于代码本身无法缩短,那么我们希望编译时间尽可能短,这样,才不会把生命都浪费在等待代码编译上。
除了刚才提到的模板问题之外,CppUnit会把所有测试编译生成一个可执行文件,这意味着什么?几乎修改任何一个文件都会造成这个文件的重新生成。随着目标文件的增加,这个过程时间就会增长。相对于修改范围(可能只是某一个文件)。
是显得有些长了。为什么Java语言不会存在这种现象?因为Java是动态连接的,所以,Java生成.class就结束了。对应到C++上,这只是完成了目标文件的生成,而在C++我们不得不再进一步生成可执行文件。
从道理上,C++单元测试可以为不同的测试文件生成不同的可执行文件,不过这么做又少了总体的过程,统计起来又显得心有余力不足了,而且通常不会这么做。个人而言,对这几个单元测试框架都不是非常了解,如果前面的讨论存在谬误,欢迎有识之士指出。