您好,欢迎访问三七文档
当前位置:首页 > 行业资料 > 交通运输 > CppUnit-code
名称CppUnit源码解读作者晨光(Morning)简介本教程整理自站长的CppUnit源码阅读笔记,CppUnit是自动化单元测试框架的c++实现版本。如何将诸多技术综合运用到一个实际的框架中来,CppUnit为我们提供了一个难易适中的参考范例。在这里,我们可以看到STL、DesignPattern的灵活运用。希望可以通过站长的讲解,使大家能够从中汲取有益的营养。声明本教程版权为晨光(Morning)所有,未经允许,请勿复制、传播,谢谢。()原文地址序言[引言]记得2003年的春节假期,难得有时间可以静下来充充电,于是有了研读CppUnit源码的念头。一来是为了熟悉CppUnit的使用环境,而来也是希望通过研读源码汲取有益的东西,这一系列的文章便是整理自笔者当初的源码阅读笔记。如何将诸多技术综合运用到一个实际的framework中来,笔者以为,CppUnit为我们提供了一个难易适中的参考范例。这应该是一个很好的例子,因为它不甚复杂,却汇聚了一个framework所必需的某些设计思想以及实现技巧。在这里,我们可以看到STL的实际使用(包括一些简单的traits技法),DesignPattern的灵活运用(比如:Composite,Factory,Decorator,Singleton,Observer等)。当然,也应该指出,由于CppUnit还在不断改进中,其代码中未免还有“败笔”及不尽如人意之处。但是,瑕不掩瑜,并且从中我们也可以感受到一个成熟框架的演进过程。由于有过一点framework的设计经验和体会,笔者在阅读CppUnit源码的过程中,时常能有共鸣,并且对于框架的设计者在某些细节的处理方法,也深以为然,偶尔也有“英雄所见略同”的感叹。希望可以通过笔者的讲解,使大家也能够同样有亲历之感。[CppUnit的简单身世]CppUnit是xUnit系列中的c++实现版本,它是从JUnit移植过来的,第一个移植版本由MichaelFeathers完成,相关信息可以在找到。它是操作系统相关的,随后,JeromeLacoste将之移植到了Unix/Solaris,在上述连接中也能找到该版本的相关信息。CppUnit项目就是基于这些版本建立起来的。有关CppUnit的讨论可以在找到,在那里你还可以找到CppUnit先前的版本以及许多其它操作系统环境下的移植版本。这个库受GNULGPL(LesserGeneralPublicLicense)的保护。作者包括:EricSommerlade(sommerlade@gmx.net),MichaelFeathers(mfeathers@objectmentor.com),JeromeLacoste(lacostej@altern.org),J.E.Hoffmann,BaptisteLepilleur,BastiaanBakker,SteveRobbins这里所选用的是CppUnit1.8.0版,你可以从下载到最新版本。[CppUnit的总体构成]作为一个完整的CppUnitframework,虽然源码所在的实际路径可能不尽相关,但从逻辑上讲它们被划为如下几个部分:core:CppUnit的核心部分output:掌管结果输出helper:一些辅助类extension:作为单元测试的延伸,对CppUnitcore部分的扩展(比如:常规测试,重复测试)listener:监视测试进程和测试结果textui:一个运行单元测试的文本环境portability:提供针对不同平台的移植设置上述所有的内容均被置于CppUnit名字空间之内。[几点说明]本文主要内容依据CppUnit源码而来,部分内容还来自于源码自身所附的注释、ChangeLog等本文只作源码解读,至于xUnit家族的相关背景及基本知识笔者不准备叙述,读者可以参看相关文章对于文中所涉及的DesignPattern,Refactoring,STL等相关知识,请读者参看相关资料。除了文章本身,文中所列源码,也夹带了morning的一些注释,用以进一步说明代码意图,注释中方括号内为morning的疑问为了节省篇幅、简化内容、突出主题,文中未列出全部代码,而是有选择的给出部分代码由于工作的缘故,撰写这一系列的文章是陆续进行的,因此文字斟酌、行文的前后一致性方面不甚考究,在此请诸位见谅。如有必要且时间允许,morning将会对此作一完整的整理。2核心部分(Core)基本测试类在CppUnit中,有一个贯穿始终的最基本的pattern,那便是CompositePattern。在GoF中对该pattern有如下描述:将对象组合成树形结构以表示“部分-整体”的层次结构。Composite使得用户对单个对象和组合对象的使用具有一致性。在CppUnit的框架中,测试类分为两种,某些测试类代表单个测试,比如稍后讲到的TestCase(测试用例),另一些则由若干测试类共同构成,比如稍后讲到的TestSuite(测试包)。彼此相关的TestCase共同构成一个TestSuite,而TestSuite也可以嵌套包含。两者分别对应CompositePattern中的Leaf和Composite。[Test]相关文件:Test.h这是所有测试类的抽象基类,规定了所有测试类都应该具有的行为,对应于CompositePattern中的Component,除了标准的virtualdtor外,还定义了四个纯虚函数://运行测试内容,并利用传入其内的TestResult搜集测试结果,类似Component的Operation操作virtualvoidrun(TestResult*result)=0;//返回当前包含的测试对象的个数,若为TestCase,则返回1。virtualintcountTestCases()const=0;//返回测试的名称,每个测试都有一个名称,就像是标识,用以查找或显示virtualstd::stringgetName()const=0;//本测试的简短描述,用于调试输出。//对测试的描述除了名称外,可能还有其他信息,//比如:一个名为“complex_add”的测试包可能被描述成“suitecomplex_add”virtualstd::stringtoString()const=0;[TestFixture]相关文件:TestFixture.h该类也是抽象类,用于包装测试类使之具有setUp方法和tearDown方法。利用它,可以为一组相关的测试提供运行所需的公用环境(即所谓的fixture)。要实现这一目的,你需要:从TestFixture派生一个子类(事实上,一般的做法是从TestCase派生,这样比较方便,具体见后)定义实例变量(instancevariables)以形成fixture重载setUp初始化fixture的状态重载tearDown在测试结束后作资源回收工作此外,作为完整的测试类,还要定义一些执行具体测试任务的测试方法,然后使用TestCaller进行测试。关于TestCaller,在helper部分将会讲到。因为每个测试对象运行在其自身的fixture中,所以测试对象之间不会有副作用(sideeffects),而测试对象内部的测试方法则共同使用同一个fixture。来看一下TestFixture的定义,除了标准的virtualdtor外,还定义了两个纯虚函数://在运行测试之前设置其上下文,即fixture//一般而言setUp更为重要些,除非实例变量创建于heap中,否则其资源的回收就无需手工处理了virtualvoidsetUp(){};//在测试运行结束之后进行资源回收virtualvoidtearDown(){};[TestCase]相关文件:TestCase.h,TestCase.cpp派生自Test和TestFixture(多重继承),兼具两者特性,用于实现一个简单的测试用例。你所要做的就是派生该类,并重载runTest方法。不过通常你不必如此,而是使用TestCaller结合TestFixture的方法,这样很方便。当你发现TestCaller无法满足,你需要重写一个功能近似的类时,再使用TestCase也不迟。关于TestCaller,在helper部分将会讲到。TestCase中最重要的方法是run方法,来看一下代码,并请留意morning的注释:voidTestCase::run(TestResult*result){//不必关心startTest的具体行为,在讲到TestResult时自然会明白//末尾的endTest亦是如此result-startTest(this);try{//设置fixture,具体内容需留待派生类解决//可能有异常抛出,处理方式见后setUp();//runTest具有protected属性,是真正执行测试的函数//但具体行为需留待派生类解决try{runTest();}//在运行测试时可能会抛出异常,以下是异常处理catch(Exception&e){//PrototypePattern的一个应用//e是临时对象,addFailure调用之后即被销毁,所以需要创建一个副本Exception*copy=e.clone();result-addFailure(this,copy);}catch(std::exception&e){//异常处理的常用方法——转意result-addError(this,newException(e.what()));}catch(...){//截获其余未知异常,一网打尽Exception*e=newException(caughtunknownexception);result-addError(this,e);}//资源回收try{tearDown();}catch(...){result-addError(this,newException(tearDown()failed));}}catch(...){result-addError(this,newException(setUp()failed));}result-endTest(this);}可以看到,run方法定义了一个测试类运行的基本行为及其顺序:setUp:准备runTest:开始tearDown:结束而TestCase作为抽象类无法确定测试的具体行为,因此需要留待派生类解决,这就是TemplateMethodPattern。事实上,该pattern在framework中是很常见的。因此一个完整测试的简单执行方法是,从TestCase派生一个类,重载相关方法,并直接调用run方法(正如TestFixture中所提到的)。有意思的是,TestCase中还有run的另一个版本,它没有形参,而是创建一个缺省的TestResult,然后调用前述run方法。不过好像没怎么用到,大概是先前调试时未及清理的垃圾代码,也难怪会有“FIXME:whatisthisfor?”这样的注释了。TestCase有两个ctor:TestCase(std::stringName);//测试类的名称TestCase();后者主要用于TestCaller,因为在使用TestCaller时,需要一个defaultctor此外,TestCase将copyctor和operator=声明为private属性,以防止误用。[TestSuite]相关文件:TestSuite.h,
本文标题:CppUnit-code
链接地址:https://www.777doc.com/doc-4851638 .html