CPPUNIT是一个测试驱动开发的测试框架。所谓测试驱动开发(TDD)是一种可以在开发过程中控制忧虑感的开发方法,它坚持以测试作为开发过程的中心,在开发前根据对将要开发的程序的要求,先写好所有测试代码,并且在开发过程中不断地通过运行测试代码来获得所开发的代码与所要求的结果之间的差距。
CPPUNIT是XUNIT的一部分,它是由JUNIT演变过来的,专门针对C/C++的单元测试工具。
测试驱动开发的原则:
Ø 先写测试代码,然后编写符合测试的代码。至少做到完成部分代码后,完成对应的测试代码;
Ø 测试代码不需要覆盖所有的细节,但应该对所有主要的功能和可能出错的地方有相应的测试用例;
Ø 发现 bug,首先编写对应的测试用例,然后进行调试;
Ø 不断总结出现 bug 的原因,对其他代码编写相应测试用例;
Ø 每次编写完成代码,运行所有以前的测试用例,验证对以前代码影响,把这种影响尽早消除;
Ø 不断维护测试代码,保证代码变动后通过所有测试;
Ø 在编码前:他可以强迫你对需求进行详细的分析。
Ø 在编码时:他可以使你对over coding保持警觉。
Ø 在重构时:可以确保新的设计能够兼容旧版本的功能。
Ø 在团队开发时:可以确保自己的单元是无误的。
从CPPUNIT的概念:测试框架,我们可以获知一点CPPUNIT原理的信息。下面我们就这个概念来进一步说明CPPUNIT的原理。CPPUNIT的测试对象可以是一个函数,一个对象或是若干个对象集,在CPPUNIT中将测试对象定义为FIXTURE,确定了FIXUTRE之后CPPUNIT要做的步骤主要是:
1了解需求,从使用者的角度写出测试代码,组织好测试框架。包括对FIXTURE及相关的初始化,准备好测试用例,明确每个测试用例的预期输出结果(也就是期望值)。
2往测试代码中加入被测对象,运行测试代码后检查输出结果。
3如果随着FIXTURE的变动需要增加或者是删除测试用例,可以直接在测试代码中增加或删除,而其它测试用例可以继续延用,作为回归的测试用例。
4每一次测试CPPUNIT都是通过对比FIXTURE的返回值和期望值而得出该用例的执行是否成功,如果二者不同,则返回FALSE。
在CPPUNIT中Test Case(测试用例)是最小的单位,当一个FIXTURE有多个Test Case时,可以将这多个Test Case组成一个Test Suite,这个Test Suite是用来测试同一被测单元的一组Test Case。若需要同时测试多个对象,将所有测试用例一起执行的时候,可以将若干个Test Suite组成一个Test Factor。如果在测试的过程中FIXTURE发生了改变,可以直接修改相关的Test Case,这就是CPPUNIT对测试用例的管理。
Cppunit 支持多平台,其实程序自身带的文档就已经非常详尽了,建议在使用前能把根目录下的INSTALL,INSTALL-WIN32.TXT,INSTALL-UNIX 几个文件先过一遍,更高级的引用可以到doc目录下查找。
1) 总体构成
CppUnit是开源的测试工具,可以在它的安装包里直接看到它的源代码。
作为一个完整的CppUnit framework,虽然源码所在的实际路径可能不尽相关,但从逻辑上讲它们被划为如下几个部分:
• core:CppUnit的核心部分
• output:掌管结果输出
• helper:一些辅助类
• extension:作为单元测试的延伸,对CppUnit core部分的扩展(比如:常规测试,重复测试)
• listener:监视测试进程和测试结果
• textui:一个运行单元测试的文本环境
• portability:提供针对不同平台的移植设置
上述所有的内容均被置于CppUnit名字空间之内。
2)WINDOWS的安装以及配置
At the current time, the only supported WIN32 platform is
Microsoft Visual C++. You must have VC++ 6.0 at least.
Windows 编译环境要求我们至少是vc++ 6.0
Quick Steps to compile & run a sample using the GUI TestRunner:
- Open examples/examples.dsw in VC++ (contains all the samples).
VC7 will ask you if you want to convert, anwser 'yes to all'.
- Make HostApp the Active project
- Compile
- For Visual Studio 6 only:
- in VC++, Tools/Customize.../Add-ins and macro files/Browse...
- select the file lib/TestRunnerDSPlugIn.dll and press ok to register
the add-ins (double-click on failure = open file in VC++).
- Run the project
先编译好cppunit带的库,然后把库的路径加入到项目路径中,然后才能在例子中调用这些库。
附录2有一些cppunit自己带的文件,以及编译一些库的介绍,我们如果仔细看看它的configure文件的话,会发现它支持多平台也是在这里实现的,他会检测你运行的环境,然后相应的选择合适的工具。
3) UNIX平台
在UNIX平台下先从http://www.sourceforge.net 获得安装包,解压后在CPPUNIT的目录依次操作:
./configure //生成Makefile文件
./make
./make install //将生成的库文件复制到/user/local/lib目录下,如果没有目录的安装权限,可以指定到自己的目录下。
将安装包include/cppunit 目录复制到/usr/include目录下,里面是cppunit的头文件。
网上有很多资料都说make install之后要在共享动态库配置文件中将lib文件的目录加进去,但如果编译出来的是静态库文件,则可省略了。
以下以一个helloworld的实例讲解CPPUNIT的使用,以下例子仅作参考,注释部分表达得不很严谨,仅作参考。
#include "cppunit/Portability.h"
#include "cppunit/TestAssert.h"
#include "cppunit/extensions/TestFactoryRegistry.h"
#include "cppunit/extensions/HelperMacros.h"
#include "cppunit/TestResult.h"
#include "cppunit/TestRunner.h"
#include "cppunit/TestResultCollector.h"
#include "cppunit/BriefTestProgressListener.h"
class Test:public CPPUNIT_NS::TestCase //定义测试用例
{
CPPUNIT_TEST_SUITE(Test); //创建一个suite,并将Test添加到suite
CPPUNIT_TEST(testHelloWorld); //声明一个测试用例testHelloWorld,如果需要增加测试用例也需要在这里声明。
CPPUNIT_TEST_SUITE_END(); //结束suite声明
public:
void setUp(void) //CPPUNIT提供的初始化方法,将需要初始化的变量等在这里定义赋值。类似于构造函数,在执行testcase之前自动调动。本实例没有需要初始化的变量。
{
}
void tearDown(void) //CPPUNIT提供的结束testcase执行完之后自动运行的方法,如,需要清除的指针等。testcase执行完后自动调用,类似于虚构函数。本例不需要作清除工作,也可以不定义
{
}
protected:
void testHelloWorld(void){ //测试用例testHelloWorld,如果需要增加测试用例,可以 续添加
printf("Hello,World!\n");
}
};
CPPUNIT_TEST_SUITE_REGISTRATION(Test); //通过宏注册Test到test suite
int main()
{
CPPUNIT_NS::TestResult controller; //保存测试结果
CPPUNIT_NS::TestResultCollector result; //收集测试用例的执行结果,它能够区分测试用例执行结果是false或是出现了错误。
controller.addListener(&result);
CPPUNIT_NS::BriefTestProgressListener progress;
controller.addListener(&progress);
CPPUNIT_NS::TestRunner runner; // TestRunner用于执行测试用例,它将待执行的测试对象管理起来,然后供用户调用
runner.addTest(CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest());
runner.run(controller);
return result.wasSuccessful()?0:-1; //反回用例的执行结果,如果成功返回0失败返回-1
}