一、CPPUNIT概念
CPPUNIT是一个测试驱动开发的测试框架。所谓测试驱动开发(TDD)是一种可以在开发过程中控制忧虑感的开发方法,它坚持以测试作为开发过程的中心,在开发前根据对将要开发的程序的要求,先写好所有测试代码,并且在开发过程中不断地通过运行测试代码来获得所开发的代码与所要求的结果之间的差距。
CPPUNIT是XUNIT的一部分,它是由JUNIT演变过来的,专门针对C/C++的单元测试工具。
测试驱动开发的原则:
Ø先写测试代码,然后编写符合测试的代码。至少做到完成部分代码后,完成对应的测试代码;
Ø测试代码不需要覆盖所有的细节,但应该对所有主要的功能和可能出错的地方有相应的测试用例;
Ø发现 bug,首先编写对应的测试用例,然后进行调试;
Ø不断总结出现 bug 的原因,对其他代码编写相应测试用例;
Ø每次编写完成代码,运行所有以前的测试用例,验证对以前代码影响,把这种影响尽早消除;
Ø不断维护测试代码,保证代码变动后通过所有测试;
Ø在编码前:他可以强迫你对需求进行详细的分析。
Ø在编码时:他可以使你对over coding保持警觉。
Ø在重构时:可以确保新的设计能够兼容旧版本的功能。
Ø在团队开发时:可以确保自己的单元是无误的。
二、CPPUNIT原理
从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的安装使用
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
}
#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
}
延伸阅读:CppUnit体验:安装 配置与编码测试
C/C++的手写工作还是比较繁重。而且单元测试对软件成熟度要求非常高,如果你们的需求整天变来变去的,你不要说测试了,能完成修改就阿弥陀佛了。至于BUG估计暂时都顾不上了。我举个简单的例子来说明C/C++的测试任务量。用python写一个协议测试工作,协议里有上百个消息。没接触过这个协议的熟手只需要一星期。但是用C/C++写,起码两星期(字节序,字节数,异常判断处理),还不包括调试,编译时间。C/C++的编译也是非常耗时间的,而且这个时间有累计效应。
好多介绍cppUnit使用方法的文章都大同小异,其实其介绍还是趋于理论化一些,我刚看到类似的文章的时候也还是不知道到底该怎么用cppUnit,现在将我的使用方法写出来给大家参考,可能有一些笔误和记错的地方,但大体流程和步骤还是对的。以cppUnit为例。
1、将cppUnit编译通过,需要配置的地方都配置好(网上有很多文章都讲了这个);
2、建立一个工程(比如vc,建立一个基于Dialog的工程);(cppUnit有两种输出界面,我个人比较趋向于对话框的这种)
3、在app文件中加入RunUnitTests()函数,函数里边的内容在cppUnit自带的例子程序中就有;
4、删除调用对话框的那段代码,换成RunUnitTests(),这样你的输出界面就是cppUnit显示的那个界面了;
5、建立你自己的测试类,其基类为CPPUNIT_NS::TestFixture;当然要添加相应的cppunit头文件,在cppUnit自带的例子程序中就可以找到;
6、加入测试函数,然后在测试类的.h文件中加入TestSuite(); Test_Suite_End()宏,将你的测试函数也添加进去;
7、这样cppUnit的测试环境就基本建立好了,把你要测试的文件添加进来,并配置相应的头文件。
8、在你的测试函数中添加测试代码;
Project->Add To Project->Files…
由于单元测试是“隔离”的测试,对已开发的大量的代码进行单元测试往往是很困难的,最好是边开发边测试。测试已存在的大量代码时,如果把产品文件一古脑地加入测试工程,能通过编译的可能性很小,最好一个一个加,加一个就编译一下看有没有问题,并且,加入的顺序应该是从底层类开始,最好找一个工具,先对文件按底层到高层进行排序,然后再按顺序测试。
比如在VC中,就可以把被测试的文件加入到你建立的测试工程中,然后在你新建的测试类中包含被测试文件的头文件。然后根据被测试文件需要的环境配置好(就是编译通过)。这样你就可以在你的测试类中建立针对被测文件中某一函数的测试函数了。