如何使用CppUnit进行单元测试

发表于:2008-8-16 14:10

字体: | 上一篇 | 下一篇 | 我要投稿

 作者:hmf3000    来源:VC知识库

二、概念

  在使用之前,我们有必要认识一下CppUnit中的主要类,当然你也可以先看后面的例子,遇到问题再回过头来看这一节。

  CppUnit核心内容主要包括一些关键类:

  Test:所有测试对象的基类。

  CppUnit 采用树形结构来组织管理测试对象(类似于目录树,如下图所示),因此这里采用了组合设计模式(Composite Pattern),Test的两个直接子类TestLeaf和TestComposite分别表示“测试树”中的叶节点和非叶节点,其中 TestComposite主要起组织管理的作用,就像目录树中的文件夹,而TestLeaf才是最终具有执行能力的测试对象,就像目录树中的文件。

  Test最重要的一个公共接口为:

  virtual void run(TestResult *result) = 0;

  其作用为执行测试对象,将结果提交给result。

  在实际应用中,我们一般不会直接使用Test、TestComposite以及TestLeaf,除非我们要重新定制某些机制。

  TestFixture:用于维护一组测试用例的上下文环境。

  在实际应用中,我们经常会开发一组测试用例来对某个类的接口加以测试,而这些测试用例很可能具有相同的初始化和清理代码。为此,CppUnit引入TestFixture来实现这一机制。

  TestFixture具有以下两个接口,分别用于处理测试环境的初始化与清理工作:

  virtual void setUp(); 
  virtual void tearDown();


  TestCase:测试用例,从名字上就可以看出来,它便是单元测试的执行对象。

  TestCase从Test和TestFixture多继承而来,通过把Test::run制定成模板函数(Template Method)而将两个父类的操作融合在一起,run函数的伪定义如下:

// 伪代码 
void TestCase::run(TestResult* result)
{
result->startTest(this); // 通知result测试开始
if( result->protect(this, &TestCase::setUp) ) // 调用setUp,初始化环境
result->protect(this, &TestCase::runTest); // 执行runTest,即真正的测试代码
result->protect(this, &TestCase::tearDown); // 调用tearDown,清理环境
result->endTest(this); // 通知result测试结束
}

  这里要提到的是函数runTest,它是TestCase定义的一个接口,原型如下:
  virtual void runTest(); 

  用户需从TestCase派生出子类并实现runTest以开发自己所需的测试用例。
另外还要提到的就是TestResult的protect方法,其作用是对执行函数(实际上是函数对象)的错误信息(包括断言和异常等)进行捕获,从而实现对测试结果的统计。

  TestSuit:测试包,按照树形结构管理测试用例

  TestSuit是TestComposite的一个实现,它采用vector来管理子测试对象(Test),从而形成递归的树形结构。

  TestFactory:测试工厂

  这是一个辅助类,通过借助一系列宏定义让测试用例的组织管理变得自动化。参见后面的例子。

  TestRunner:用于执行测试用例

  TestRunner将待执行的测试对象管理起来,然后供用户调用。其接口为:

  virtual void addTest( Test *test ); 
  virtual void run( TestResult &controller, const std::string &testPath = "" );

  这也是一个辅助类,需注意的是,通过addTest添加到TestRunner中的测试对象必须是通过new动态创建的,用户不能删除这个对象,因为TestRunner将自行管理测试对象的生命期。

三、CppUnit 的使用

  以上工作完成以后,就可以正式使用CppUnit了,由于单元测试是TDD(测试驱动开发)的利器,一般人会先写测试代码,然后再写产品代码,不过笔者认为先写产品代码框架后再写测试代码,然后通过慢慢补充产品代码以使得能通过测试的方法会好些。不管先写谁只要写得舒服安全就可以。本文决定先写测试代码。

  前面我们提到过,CppUnit最小的测试单位是TestCase,多个相关TestCase组成一个TestSuite。要添加测试代码最简单的方 法就是利用CppUnit为我们提供的几个宏来进行(当然还有其他的手工加入方法,但均是殊途同归,大家可以查阅CppUnit头文件中的演示代码)。这几个宏是:

    CPPUNIT_TEST_SUITE() 开始创建一个TestSuite;
  CPPUNIT_TEST() 添加TestCase;
  CPPUNIT_TEST_SUITE_END() 结束创建TestSuite;
  CPPUNIT_TEST_SUITE_NAMED_REGISTRATION() 添加一个TestSuite到一个指定的TestFactoryRegistry工厂。

  感兴趣的朋友可以在HelperMacros.h看看这几个宏的声明,本文在此不做详述。

  假定我们要实现一个类,类名暂且取做CPlus,它的功能主要是实现两个数相加(多简单的一个类啊,这也要测试吗?不要紧,我们只是了解怎样加入测试代码来测试它就行了,所以越简单越好)。 假定这个类要实现的相加的方法是:

  int Add(int nNum1, int nNum2);

  OK,那我们先来写测试这个方法的代码吧。TDD 可是先写测试代码,后写产品代码(CPlus)的哦!先写的测试代码往往是不能运行或编译的,我们的目标是在写好测试代码后写产品代码,使之编译通过,然后再进行重构。这就是Kent Beck说的“red/green/refactor”。所以,上面的类名和方法应该还只是在你的心里,还只是你的idea而已。

  根据测试驱动的原理,我们需要先建立一个单元测试框架。我们在VC中为测试代码建立一个project。通常,测试代码和被测试对象(产品代码)是处于不同的project中的。这样就不会让你的产品代码被测试代码所“污染 ”。

  由于在CppUnit下, 可以选择控制台方式和UI方式两种表现方案,我们选择UI方式。在本例中,我们将建立一个基于GUI 方式的测试环境。因此我们建立一个基于对话框的Project。假设名为UnitTest。

  建立了UnitTest project之后,我们首先配置这个工程。

52/5<12345>
《2023软件测试行业现状调查报告》独家发布~

关注51Testing

联系我们

快捷面板 站点地图 联系我们 广告服务 关于我们 站长统计 发展历程

法律顾问:上海兰迪律师事务所 项棋律师
版权所有 上海博为峰软件技术股份有限公司 Copyright©51testing.com 2003-2024
投诉及意见反馈:webmaster@51testing.com; 业务联系:service@51testing.com 021-64471599-8017

沪ICP备05003035号

沪公网安备 31010102002173号