CppUnit 介绍

cppunit原理分析以及例子 1、cppunit是什么 2、cppunit组成    在 CppUnit 中,一个或一组测试用例的测试对象被称为Fixture。Fixture 就是被测试的目标,可能是一个对象或者一组相关的对象,甚至一个函数。   有了被测试的Fixture,就可以对这个Fixture的某个功能、某个可能出错的流程编写测试代码,这样对某个方面完整的测试被称为TestCase。 阅读详情

某种意义上说,CppUnit的代码并不是很好的C++代码。正因为它不是很好的C++代码,并且代码量不是很大(主库80K),所以我觉得比较适合想大量使用CppUnit并且需要深入了解的人或是初步涉足C++,想阅读一些简单的源代码/库的人。

这篇文章不适合于从未使用过CppUnit的人,如果你从未使用过CppUnit,但是对于测试驱动开发很感兴趣,可以参阅我的另一篇文章:CppUnit入门。

I. 目的

前一段时间拿到公司新的引擎,发现里面使用了CppUnit来做单元测试,于是小小的研究了一把,顺便把阅读CppUnit代码的心得写下,和大家分享。某种意义上说,CppUnit的代码并不是很好的C++代码,这主要是由历史原因引起的:首先CppUnit是对JUnit的一个移植,所以很多地方是把C++作为Java在用,从风格上到语义上,都是Java的;当然这也无可厚非,因为对于移植来说,只要能够做到和原先一样用起来,别的都是第二位的。其次,CppUnit(过度的?)考虑了兼容性,为了在不同的C++编译器和标准库下都能使用,它给自己制定了很多编码标准,譬如说"不要使用mutable"、"不要使用typename"、甚至"不要使用STL容器中的at成员",...。正因为它不是很好的C++代码,并且代码量不是很大(主库80K),所以我觉得比较适合想大量使用CppUnit并且需要深入了解的人或是初步涉足C++,想阅读一些简单的源代码/库的人(如果你很有经验,想学习最新的C++技术,或是想阅读大型的库,那么你可以阅读别的开源库。)

这篇文章不适合于从未使用过CppUnit的人,如果你从未使用过CppUnit,但是对于测试驱动开发很感兴趣,可以参阅我的另一篇文章:CppUnit入门。

II. 代码

CppUnit作为一个UnitTest框架,它的主要机制就是:让你为每一段代码写出测试用例,并且把大量的测试用例按照某种方式管理起来,并且提供给你不同的界面以及输出方式。所以我觉得它的代码也可以分为相应的部分:
1.测试用例及其管理,这部分只是管理和测试相关的数据结构,它们往往是静态的初始化的,在真正的测试还没有进行前就已经完全执行完毕了。
2.实际进行测试的代码,这部分包含了测试的初始化,测试中的异常处理以及测试进程和结果的通知等。这部分代码是你的测试运行中所执行的代码。
3.测试的界面和输入。譬如说,CppUnit提供了一个基于MFC的测试界面,你可以选择一些测试用例进行测试,并且获得结果。这部分代码我们将不作详细介绍,因为和测试本身其实关系不大。

III. 测试用例及其管理

这部分的类有Test、TestLeaf、TestCase、TestComposite和TestSuite以及一些像TestPath之类的辅助类。这一部分代码其实和UnitTest本身毫无关系,它只是一个对象树的管理。CppUnit把测试系统中所有可以用来测试的对象作为一棵树进行管理,每一个测试都是树上的一个节点,这些节点都是以Test为父类的。

1. TestPath

如果把整个测试用例树比作为文件树的话,TestPath表示的是一个相对/绝对路径。它内部保存的是一个Test的队列,也就是说,它把一个类似于/root/suite/case或者是/suite/child/case的路径变为一个路径上所经过的Test对象的队列。通过这个结构,我可以很方便的在树结构中的节点间随意移动。

2. Test

Test类中,除了虚析构函数和virtual void run( TestResult *result )=0 这个纯虚函数用来执行测试以外,别的函数都是用来做树管理的。因为树是递归定义的,作为一个节点,它只需要能够枚举自身的子节点就可以了。这些相关的函数为:countTestCases用来返回这个节点及其子节点中包含了多少个有效的Test对象,这样做主要是处于计数的目的,因为Test树上很多节点本身并不包含任何测试代码;getChildTestCount返回直接的子节点个数;getChildTestAt接受一个索引,返回相应的子节点;getName返回这个节点的名字。上述函数都是纯虚函数,由子类给出具体定义。
getChildTestAt是一个虚函数,它内部实际调用的是checkIsValidIndex和doGetChildTestAt,并且CppUnit的设计者也建议你不要重载它,而是重载doGetChildTestAt函数。这个是GoF里面的Template Method模式,不过既然这里不建议重载,就不应该使用virtual函数。或者和可能是JUnit的影子。
Test类真正实现的功能是对非直接子节点的处理。如前所述,对于直接子节点可以通过一个index来表示,而对于非直接子节点则是通过TestPath来表示的。Test提供了findTestPath、findTest以及resolveTestPath函数来对这些功能提供支持。虽然这些函数是以虚函数的形式提出的,也就是说理论上可以进行扩展,但是事实上在整个CppUnit体系中,只有Test类对它们作了实现。

3. TestLeaf

TestLeaf是Test的子类,它代表了Test树上的一个叶节点。它的实现很简单,对于countTestCases返回1,因为它自身就是一个有效的Test;getChildTestCount则返回0,因为没有子节点;它的doGetChildTestAt在正常情况下根本不应该被调用。

4. TestComposite

TestComposite也是Test的子类,它代表了Test树上的一个非根节点,这里对它的使用类似于一个GoF中Composite模式。它的countTestCases返回的是对所有子节点的countTestCases的累加,从这点上可以看出,TestComposite本身只是一个Test的容器,不作为一个有效的Test。TestComposite的run函数会调用自身的doStartSuite函数,在这个函数中,会对TestResult的startSuite进行调用;然后run函数再通过doRunChildTests函数间接调用所有子节点的run,最后通过doEndSuite函数间接调用TestResult的endSuite函数。TestComposite的run函数其实也是一个Template Method模式,因为通常希望通过这三个do****函数来对其进行定制。对于TestRunner::startSuite/endSuite的调用是为了让传入的TestRunner在每个Suite被测试的前后都得到通知,以做一些簿记工作。(当然窃以为这里的名字最好叫startComposite/endComposite。)

5. TestCase

TestCase继承自TestLeaf和TestFixture,是整个CppUnit中最常用最重要的类之一。顾名思义,TestCase就代表了通常UnitTest中所指的测试用例,也是整个Test树中最常用的叶节点。这个类既在Test树中有它的位置,也直接的参与测试的进行,所以被放在下一节介绍。

6. TestSuite

TestSuite是从TestComposite继承而来的,TestComposite为那些非根节点提供了运行机制,而TestSuite则在此基础上提供了对于子节点的管理。譬如说,通过addTest函数可以加入Test,deleteContents删除所有的子Test对象,它还实现了getChildTestCount和doGetChildTestAt函数以返回子节点的个数和指针。

IV. 实际进行测试的代码

这些类包括TestFixture、TestCase、TestCaller、TestRunner、TestListener、TestResult和Protector类体系。

1. TestFixture
与其说Test类是所有Test的根,不如说TestFixture是CppUnit中所有"测试"的根。因为仅仅从Test类继承而来的类只是这个体系的"管理部门",而从TestFixture继承而来的类,才是真正进行测试的"执行部门"。TestFixture除了一个虚析构函数(C++中"请从我派生"的代名词)以外,就定义了两个虚函数:setUp和tearDown,前者初始化一次测试,后者清除一次测试所产生的所有副作用。这三个函数在TestFixture中的定义都是空的。

2. TestCase
这是我们第二次看到它了,因为它处于两个类体系的交汇点,使用多重继承从TestLeaf和TestFixture继承。这个类既没有对TestLeaf中对于作为Test树子节点方面的功能进行加强,也没有重新定义TestFixture中为空的setUp和tearDown。唯一做的就是定义了run。它的run里面,会先调用TestRunner::startCase,然后调用setUp,接着调用TestCase中新定义的runTest函数,接着调用tearDown和TestRunner::endCase。从注释中可以看出作者希望把runTest作为一个纯虚函数定义,也就是说,其实你可以从TestCase派生,并且定义一个自己的runTest函数,以进行一些简单/单一的测试工作。如果要进行复杂的测试工作/构建复杂的测试用例树,那么应该使用别的机制。这样说的原因有两方面:首先从管理角度说,如果你有几项测试任务,就应该把它们作为Test树中的不同节点,这样你可以对总任务数/失败数进行统计,而TestCase是一个TestLeaf,如果你把它们堆砌在一个TestCase中,不利于管理;其次,如果你有几项测试任务要做,并且共享同样的初始化/清除代码,那么你想从TestCase派生来做这件事情,就必须重写setUp、tearDown和run。重写前两者也算了,要是连run也重写了,这,我干嘛还从TestCase派生?

3. TestCaller
TestCaller是一个GoF中的Adapter模式,它可以把任意一个定义了setUp/tearDown的类的对象包装为一个TestCase,并且在runTest中对这个对象的某个函数进行调用。也就是说,如果我有一个TestFixture的派生类FooBarTest,其中有一个fooTest和一个barTest函数,那么TestCaller(FooBarTest::fooTest)以及TestCaller(FooBarTest::batTest)就是两个TestCase派生类,它们的实例可以作为TestCase被加入到Test树中,也可以独立的进行测试运行。
TestCaller从TestCase派生而来,并且接受某个类的实例(也可以自己通过new生成一个)以及那个方法的指针作为构造函数参数并且保存在内部,在setUp和tearDown函数中,它调用了那个对象的setUp和tearDown,并且在runTest函数中使用保存的对象对它的指定的成员函数进行调用。使用TestCaller的好处是,你可以把一组相关的Test任务放在某个从TestFixture派生而来的类中,并且用TestCaller把它们包装成若干个TestCase。这样一来便于对相关的测试任务的管理,二来也能让不同的任务成为Test树的不同子节点。

4. TestListener
TestListener其实是一个测试事件的接收器,它定义了startTest、endTest、startSuite、endSuite、startTestRun、endTestRun和addFailure方法,这些都是空的虚函数,你可以定义自己的派生类并且对自己感兴趣的事件进行处理。

5. TestResult
TestResult是从SynchronizedObject继承而来的,SynchronizedObject其实就是对于Java中synchronized的模拟,SynchronizedObject和SynchronizedObject::ExclusiveZone是一个典型的用RAII来对某一个作用域进行互斥访问的例子。TestResult定义了addListener和removeListener方法来管理事件的订阅者,并且它也定义了所有Listener的方法,当你对TestResult的某个事件方法调用时,它会把这个事件发送给所有的Listener,不过CppUnit的开发者并不认为TestResult是一个TestListener,所以即使它同相同的方法实现了TestListener的所有函数,也没有从TestListener继承。
TestResult内部维护了一个测试过程是否被强行中止的标志,并且通过reset、stop和shouldStop对其进行管理,这给予运行中的测试一个响应强行中止的机会。
TestResult增加了几个函数,runTest就是其中之一,它接收一个Test的指针作为参数,并且调用这个Test对象的run。当你有一个Test对象和一个TestResult对象的时候,你可以通过Test::run(TestResult*)或者TestResult::runTest(Test*)来完成一次测试任务,区别在于后者在调用Test::run的前后会对TestResult::startTestRun和TestResult::endTestRun进行调用。
另一些比较有趣的函数是protect、pushProtector和popProtector。这三个函数其实是维护了一个ProtectorChain对象,在protect中调用了ProtectorChain::protect来为测试提供一个受保护的环境。

6.  Protector类体系
大家都知道,我们运行某个测试任务时,可以根据返回值来判断成功还是失败,可是有些"不良"函数会抛出异常,我们必须对异常进行捕捉,否则就不能进入下一个测试而会提前用一种极其可悲的方式结束。基本的Protector只提供了一些报错的辅助函数。通过查看DefaultProtector::protect的代码,可以得知在它的protect中,会尝试捕获Exception和std::exception,并且对所有未知的异常用...进行捕捉。DefaultProtector适合作为一个"最后的选择"来使用,因为通常我们希望知道我们是否抛出了某个或者多个特定的异常,这需要使用ProtectorChain。ProtectorChain::protect中有这样一段代码:

Functors functors;
for ( int index = m_protectors.size()-1; index >= 0; --index )
{
 const Functor &protectedFunctor = functors.empty() ? functor : *functors.back();
 functors.push_back( new ProtectFunctor( m_protectors[index], protectedFunctor, context ) );
}

这段代码中,m_protectors是一组Protector,这段代码通过ProtectFunctor来把这些Protector连接在一起,并且它的根是functor,也就是那个需要保护的函数。最后对这个functors最后一个元素调用的时候,它先建立自己的保护机制,然后调用它的functor,那个functor就是它的前一个元素,可能是另一个Protector,也可能是最原始的受保护protector,这样一来,也就是说,所有的保护被一层一层的嵌套起来。

V. 小结

其实整个CppUnit中还有不少别的有意义的代码,譬如说它的测试结果输出机制,它的那些辅助宏和TestSuite的全局注册机制等,我将在以后的文章中介绍。

使用CppUnit进行单元测试 一. CppUnit的安装      从http://sourceforge.net/projects/cppunit   CppUnit的源码包. CppUnit是开源产品 , 当前最高版本为1.11.0. (在上面的链接所指向的页面上选择 Development Snapshot ).        下载后,将源码包解压缩到本地硬盘. 以C:为例, 解压到C:/CppUnit-1.11.0 . 阅读详情

相关推荐

C++——测试工具CppUnit的安装和配置

The first port of JUnit to C++ was done by Michael Feathers. His versions can be found on the XProgramming software page. They are os-specific, so Jerome Lacoste provided a port to Unix/Solaris. His v

非语 1万+

全面了解CPPUnit

全面了解CPPUnit 一、   单元测试与CPPUNIT简介 几乎每个开发人员都写过测试代码,但是往往这些代码不系统,也没有良好的管理,同时在测试代码编写过程中也有很多重复的劳动,比较繁琐。在一个软件开发过程中,往往会进行很多修改,迭代开发的模型随处可见,如何验证程序的功能、性能和结构是否符合要求是一项重要的工作。 单元测试是属于白盒测试和结构性测试,一般由开发人员开展,当然如果有好的测试

thanklife的专栏 5010

cppunit源程序及教程

包含cppunit的源程序和两个简例的教程,新手可以用上哦。

使用CPPUnit执行C/C++单元测试

Windows环境下,使用CPPUnit执行C/C++单元测试,支持VS2013/VS2015/VS2017/VS2019开发环境,支持x86和x64。

geriletuma 1559

C++单元测试工具CppUnit使用简介

准备工作:   1. 到 https://sourceforge.net/projects/cppunit/CppUnit测试框架的源代码   2. 解压文件,进入src文件夹,打开CppUnitLibraries.dsw.分别编译这两个project,输出位置均为lib文件夹。   3.在VC的tools/optiONs/directories/include files和library files中设置include路径和lib路径。   4.右键我的电脑设定->->环境变量->系统变量->Path中,将Cppuint的目录的lib路径设置为系统搜索路径   创建测试工程:  

(实测可用)STM32L431RCT6开发板-实验1 跑马灯实验

一、开发板平台简介: 1、开发板资源简介 (1)开发板主芯片型号:STM32L431RCT6 (2)开发板主芯片封装:LQFP-64_10x10x05P (3)开发板主芯片内核:ARM® Cortex®-M4 (4)开发板主芯片主频:80MHz (5)开发板主芯片Flash大小:256KB (6)开发板主芯片RAM大小:64KB

zhej2014的博客 3167

CPPUNIT基本原理【转载】

一、CPPUNIT概念   CPPUNIT是一个测试驱动开发的测试框架。所谓测试驱动开发(TDD)是一种可以在开发过程中控制忧虑感的开发方法,它坚持以测试作为开发过程的中心,在开发前根据对将要开发的程序的要求,先写好所有测试代码,并且在开发过程中不断地通过运行测试代码来获得所开发的代码与所要求的结果之间的差距。 CPPUNIT是XUNIT的一部分,它是由JUNIT演变过来的,专门针对...

weixin_30316097的博客 187

Unity插件-Mirror使用方法(一)Mirror介绍

Unity Mirror 是一个专为 Unity 引擎设计的开源、社区驱动的网络库,主要用于简化多人联机游戏的开发。它最初是为了替代 Unity 原生的 UNet 系统(已被官方弃用)而创建的,因其轻量、灵活和高效的特点,成为 Unity 开发者构建多人游戏的热门选择。

qq_44501691的博客 4099

关于CppUnit里面宏的介绍

本文是讨论开放源码单元测试工具的 系列文章 的第 2 篇,介绍非常受欢迎的 CppUnit — 最初由 Eric Gamma 和 Kent Beck 开发的 JUnit 测试框架的 C++ 版本。C++ 版本由 Michael Feathers 创建,它包含许多类,有助于进行白盒测试和创建自己的回归测试套件。本文介绍一些比较有用的 CppUnit 特性,比如 TestCase、TestSu

工作记录--创造或收集原创 1288

告别外置天线!手把手教你用HFSS仿真设计一个20x40mm的433MHz PCB蛇形天线

本文详细介绍了如何使用HFSS仿真工具设计一个20x40mm的433MHz PCB蛇形天线,解决物联网设备小型化中的天线集成难题。通过对比外置天线与PCB天线的性能差异,深入解析蛇形天线的电磁学原理,并提供从建模到优化的全流程实战指南,帮助工程师实现高效、低成本的微型化天线设计。

weixin_30606461的博客 431

深入解析USBlyzer抓包工具:从设备描述符到数据交互实战

本文深入解析了USBlyzer抓包工具在USB协议分析中的应用。从设备描述符的抓取与解读,到设置地址、配置描述符的实战分析,再到数据交互阶段的抓包技巧,文章通过具体案例详细展示了如何使用USBlyzer透视USB总线通信,是排查设备驱动问题、逆向分析通信协议和学习USB规范的实用指南。

weixin_29171087的博客 741

收集介绍如何使用CppUnit的网址

1.编写GUI类型的CppUnit单元测试框架及用例:http://blog.youkuaiyun.com/vinchwang/article/details/766514  2.CppUnit测试框架入门:http://www.vckbase.com/index.php/wv/982 3.CppUnit在Window下的安装与使用:http://blog.sina.com.cn/s/blog_6f6ea

物知馆 620

【四足机器人项目实战】四足机器人嵌入式硬件设计

系列文章目录 提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加 TODO:写完再整理 文章目录系列文章目录前言一、电源管理板设计及制作二、通讯中继板设计及制作三、力控无刷电机驱动板设计及制作1.磁场定向控制FOC控制原理2.电机转子位置、速度检测原理3.硬件CAN通讯原理 前言 认知有限,望大家多多包涵,有什么问题也希望能够与大家多交流,共同成长! 本文先对四足机器人–嵌入式硬件设计做个简单的介绍,具体内容后续再更,其他模块可以参考去我其他文章 提示:以下是本篇文章正文内容 一、电

qq_35635374的博客 1万+

测试驱动开发方法介绍及CPPUnit使用指南

单元测试(Unit Test)是一种测试方法,用于对类,方法等进行行为验证。举一个简单的例子:如果需要测试一个累加函数int sum(int k),单元测试表现为给此函数不同的输入,然后验证对应的输出是否满足要求。如对sum(int k),给他一些输入,这些输入应该满足人脑稀奇古怪的念头,可以是sum(0);sum(5);sum(-5);sum(100000);sum(5.1234);对于这些输入

蜗牛档案室 2368

PINN驱动的3D不可压缩流场重建——从稀疏2D2C观测到完整物理场反演——含源码实现

多尺度融合策略在此发挥作用:高分辨率的2D2C数据用于约束平面内的精细结构,而低分辨率的3D3C数据提供第三维度的粗尺度信息。迁移学习架构在此发挥关键作用。这种策略在湍流封闭模型构建中尤为有效,其中RANS提供的平均流场与DNS的雷诺应力数据融合,显著提升了分离流的预测精度。在稀疏数据场景下,注意力权重根据观测数据的分布动态调整,强化数据缺失区域的物理约束,同时避免过拟合于观测点。这种机制与自适应采样的结合,实现了计算效率与重建精度的平衡,在仅保留约百分之十关键节点的情况下,仍能保持高物理一致性。

u012133341的博客 136

包络检波电路(Envelope Detector)

把 100 kHz 载波想象成"电台的高频信号",把 1 kHz 包络想象成"主持人说话的声波"。而今天电路的工作,就是把这个"调大调小的外壳"(包络)重新剥出来,还原成主持人的声音 🎵。这是三极管开关电路的重要搭档,也是从"二极管家族"过渡到"三极管家族"的桥梁 🔌。——倍压整流看的是"幅值累积"(多个电容接力),包络检波看的是"频率分离"(高低频分开)。同样的"D1 串联 + RC 并联"拓扑,今天被赋予了"AM 解调"的新生命。——电感负载的"泄洪闸",保护三极管开关不被反向电动势击穿。

飞多学堂的博客 94

设计原则与模式: 案例介绍--CppUnit

设计原则与模式: 案例介绍--CppUnitCppUnit 是一个单元测试框架, 我们看一看它的设计是如何遵循基础的设计原则和模式的单一职责原则TestRunner 和 TestResult 的分离 class CPPUNIT_API TestRunner {virtual void addTest( Test *test )

切尔斯基的杂记 1835

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

全面了解cppunit_了解CppUnit

本文是有关用于单元测试的开放源代码工具系列的第二篇文章,介绍了非常流行的CppUnit-JUnit测试框架的C++端口,该端口最初由Eric Gamma和Kent Beck开发。 C++端口是由Michael Feathers创建的,它包含各种类,可以帮助白盒测试和创建自己的回归套件。 本文介绍了一些更有用的CppUnit功能,例如TestCase,TestSuite,TestFixture,...

cusi77914的博客 1270

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

CppUnit源码解读

名称 CppUnit源码解读  作者 晨光(Morning)  简介 本教程整理自站长的CppUnit源码阅读笔记,CppUnit

蜗牛档案室 4716

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

CPPUnit用法简介

CPPUnit是一个开源的C++单元测试工具.实际上是一些dll,你要写单元测试时调用它的一些类就行.跟我们平时使用一般的dll文件类似. 下面的详细的使用步骤,参考了别人两篇博客写出来的. 把cppunit源文件编译成dll文件 1.)先去网上找个cppunit的源文件来,我找到的是cppunit-1.12.1.tar.gz,解压缩后所有的源文件都在目录cppunit-1.12.1

weiwenhp 6545
上一篇: cppunit helloworld详尽篇
下一篇: CppUnit入门实践-Tony与Alex的对话系列
pll621
博客等级 码龄25年 27粉丝 87原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值