购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

实验5
基于CppUnit的单元测试

(共4学时)

5.1 实验目的

(1)理解CppUnit单元测试工具的原理;

(2)掌握基于CppUnit的面向对象的单元测试能力;

(3)熟练使用测试覆盖率分析工具Gcov。

5.2 实验前提

(1)熟练掌握C++编程语言,了解测试用例设计的基本方法;

(2)熟悉Linux操作系统,具备CppUnit和Gcov测试工具使用的基本知识;

(3)具备Linux系统环境,能够编译、链接和运行C++开源项目。

5.3 实验内容

利用CppUnit对开源的内存数据库管理系统(FastDB)中的dbDate类进行测试,并利用Gcov工具分析测试覆盖率,在此基础上完成mock技术的原理及作用分析。

5.4 实验环境

(1)每三四个学生组成一个测试小组,其中一位同学担任组长,负责协调大家的工作。

(2)被测试开源程序(FastDB)代码要保证编译通过,可正常运行,例如,本实验中选择sourceforge开源项目中的FastDB,它是一种面向关系的嵌入式内存数据库系统,与C++程序语言高度集成。它使用了操作系统的虚拟映射机制访问数据,提供了面向对象扩展的SQL语言子集,此外,还能够支持事务处理、故障容忍和恢复功能。该开源项目的网址为https://sourceforge.net/projects/fastdb/,本实验中选择V3.75版本,FastDB软件资源信息如图5-1所示。

(3)CppUnit是xUnit单元测试框架中的组成部分,能够用于C++语言程序的单元测试。CppUnit软件属于开源项目,起初由sourceforge管理,目前由freedesktop进行维护,其网站地址为https://www.freedesktop.org/wiki/Software/cppunit/,本实验中选择V1.13.0版本,CppUnit软件资源信息如图5-2所示。

图5-1 FastDB软件资源信息

图5-2 CppUnit软件资源信息

(4)需要两台以上计算机(PC或笔记本电脑),都安装了Linux操作系统,本实验选择国产化的中标麒麟操作系统(NeoKylin 3.2.1 caramobla)。操作系统中都安装了g++编译器和Gcov工具,这可以在终端窗口中输入g++和gcov,如果显示如下信息,说明编译和覆盖率环境已就绪。

5.5 实验过程

(1)安装和使用CppUnit单元测试框架,结合源码中的样例程序(文件路径为cppunit-1.13.0\examples\simple)分析和实践基于CppUnit的单元测试,实验目的是掌握和理解CppUnit测试框架的原理以及在单元测试过程中的使用。

(2)下载和编译FastDB开源项目,利用CppUnit单元测试框架,针对dbDate类编制测试程序并进行测试执行和结果分析。在此基础上总结出面向对象程序单元测试时,编写测试程序的要点和注意事项。

(3)小组内部针对dbDate类,讨论测试用例设计的方法和注意事项,并给出设计的测试用例集(包括测试对象、前置条件、输入参数、输出结果、预期结果等信息),设计测试用例时要灵活运用前序章节中讨论的逻辑覆盖测试方法。

(4)根据前面设计的测试用例,利用CppUnit测试框架编制测试用例脚本,并执行测试用例,观察测试用例执行结果与测试设计的相符情况;测试执行时,利用Gcov软件分析代码的覆盖率情况,根据覆盖率情况对测试用例进行必要的补充。

(5)研究CppUnit的定制输出格式,能够制定的格式将特定内容写入到文本文件或XML文件中,基于上述内容编写并提交基于CppUnit的单元测试报告和学习心得。

(6)基于前面的实验过程,思考CppUnit对复杂场景(例如被测类依赖关系复杂,存在多继承、多态特性等情况)的支撑能力,分析Mock方法的原理和适用性。在此基础上,尝试使用当前流行的Mock工具,对FastDB项目中的复杂类进行测试。

5.6 实验实施

1. CppUnit下载和安装

从freedesktop官方网站下载CppUnit V1.13.0版本源码包(cppunit-1.13.0.tar.gz),在Linux系统中选择路径安装CppUnit测试框架(需要root权限),安装的主要步骤如下。

注意事项:

(1)安装成功后libcppunit相关的.o和.a文件应该被安装到/usr/local/lib路径。

(2)需要手动把cppunit-1.13.0/include/cppunit目录复制到/usr/include路径。

2. CppUnit原理分析和使用

CppUnit以及其他类似的单元测试框架大多遵循了相似的架构,都包括TestRunner、Test、TestResult、TestCase、TestSuite以及TestFixture等组成部分,如图5-3所示。

图5-3 CppUnit测试框架体系

CppUnit测试框架中各部分的用途描述如下。

(1)Test:它是CppUnit中所有测试对象的父类,如图5-4所示,用于测试的开发和管理,例如TestCase代表单个测试,而TestSuite代表多个测试。测试运行时,产生的结果则由TestResult对象进行收集管理。

图5-4 Test类的继承关系图

(2)TestCase:它代表单个测试对象,常被用于实现简单的测试用例,即通过定义子类并重载runTest方法。通常情况不会使用该类,而是使用TestFixture和TestCaller类。

(3)TestSuite:它是测试的组合,运行和管理着测试用例的集合。

(4)TestRunner:该对象用来驱动单元测试用例的执行,可以简化测试执行的工作量。

(5)TestFixture:用来提供一组测试用例的共用环境,使用setUp和tearDown方法将每个测试用例封装起来,保证两个测试用例之间的独立性。TestFixture的定义过程如下。

①根据测试对象实现TestCase的子类;

②在子类中定义与测试相关的成员变量;

③通过重载setUp方法实现对Fixture的状态初始化;

④测试用例运行后调用重载的tearDown方法清除相关资源。

(6)TestResult:该对象由TestRunner对象创建,用于处理每个测试用例的执行结果。使用TestListener或其子类获取将执行的测试,待测试完成后使用Outputter对象接收测试总结信息。TestResult对象提供了setSynchronizationObject模板方法实现多线程下的相互隔离。TestResult类的继承关系如图5-5所示。

图5-5 TestResult类的继承关系图

下面以cppunit-1.13.0\examples\simple为例,阐述使用CppUnit进行单元测试的基本过程。

1)ExampleTestCase.h内容解析

该文件中定义了ExampleTestCase类,该类重载了CPPUNIT_NS::TestFixture,能够为测试提供简洁的设置和退出机制。ExampleTestCase类重载了setUp和tearDown方法,用于对被测对象或其上下文的设置与清除,在测试运行之前调用setUp方法,在测试执行完时调用tearDown方法。

请参考CppUnit官方文档以及源代码,完成如下内容的研究:

(1)TestFixture的运行过程原理;

(2)setUP和tearDow方法的作用;

(3)CPPUNIT_TEST_SUITE、CPPUNIT_TEST和CPPUNIT_TEST_SUITE_END宏定义的用途。

CppUnit文档存放路径cppunit-1.13.0/doc/html/index.html。

2)ExampleTestCase.cpp内容解析

该文件中定义了具体的测试函数,它们是成功测试的核心内容,需要在明确被测对象需求的基础上设计出合理且充分的测试用例。测试函数中使用了几种测试断言宏,用于检查预期参数和实际参数是否匹配。

请参考CppUnit官方文档以及源代码,完成如下内容的研究:

(1)宏定义CPPUNIT_TEST_SUITE_REGISTRATION的作用;

(2)给出CppUnit提供的结果检测相关的断言宏并分析其适用场合;

(3)探讨不使用内置断言宏时如何实现测试结果判定。

3)Main.cpp内容解析

该文件中实现了测试执行过程的触发和控制,包括测试过程和测试结果监听、测试套件设置、测试运行和测试结果输出等内容。

请参考CppUnit官方文档以及源代码,完成如下内容的研究:

(1)TestResult类在CppUnit测试过程控制中的作用;

(2)TestResultCollector类如何实现测试结果采集;

(3)BriefTestProgressListener类如何实现对测试进程的监控;

(4)TestRunner类如何实现对TestSuit的增加和执行;

(5)CompilerOutputter类实现了哪些测试结果输出方式。

4)dbDate类测试程序编写

将FastDB源程序放置于中标麒麟操作系统特定路径后,在fastdb文件夹中依次执行make clean命令和make命令,确保源程序能够编译成功。

参考fastdb/examples文件夹中的样例程序,查看测试程序如何调用FastDB中的类或函数进行相关功能的测试。在此基础上,结合前面的CppUnit中simple样例程序的研究,编制dbDate类的测试程序。

基于CppUnit的单元测试的核心是编制具体的测试方法,以dbDate.isValid()方法为例,编制测试程序源文件fastDBCppUnitTest.cpp,在TestFixture的子类中(例如ExampleTestCase类)定义两个测试方法并将其增加到TestSuite,具体如下:

     CPPUNIT_TEST_SUITE(ExampleTestCase);
     ……
     CPPUNIT_TEST(testdbDateIsValid001);
     CPPUNIT_TEST(testdbDateIsValid002);
     ……
     CPPUNIT_TEST_SUITE_END();

测试方法的定义如下:

     ……
     void testdbDateIsValid001();
     void testdbDateIsValid002();
     ……

测试方法的实现如下:

在程序所在目录打开终端窗口,执行编译、链接命令后运行测试程序,结果概况如下:

3. 测试用例设计与编码

熟悉了CppUnit的框架后,请阅读并理解dbDate类的内容,查阅Gregorian calendar(公历)和Julian calendar(儒略历)的标准资料作为测试依据。结合前面实验中的逻辑覆盖测试的方法,设计测试用例完成对dbDate类的测试,要求对dbDate类的方法实现100%语句覆盖和MC/DC覆盖(若不能满足覆盖率要求则给出说明)。dbDate类的结构如下:

编制测试方法时,可以充分运用TestFixture子类的成员变量和setUp函数。

4. 测试执行和覆盖率分析

1)测试用例执行

测试方法编制完成后,执行编译、链接命令(或者编制makefile文件并执行make命令),生成并运行可执行程序,这里针对部分方法给出其测试用例运行结果,如下所示:

测试执行后,CppUnit框架给出了测试的结果,包括每个用例的执行结果(OK或Fail)、运行未通过用例的断言失败信息,以及测试用例的总数、失败用例的汇总信息。可对未通过的测试用例进行核实,确认问题后提出相应的问题报告单。

2)测试覆盖率统计

Gcov工具与GCC编译器集成使用,用来测试程序的代码覆盖率。可以和gprof一起使用以获取如下信息:

(1)每行可执行代码的执行频次;

(2)实际执行的代码的行数信息;

(3)每段代码所占用的计算时间。

了解了代码在编译过程中的作用过程,便可以确定哪些模块需要优化,Gcov能够帮助找出优化的着眼点。软件开发人员也可以通过覆盖测试来判断软件是否达到了能够发布的状态,并根据覆盖测试情况补充测试用例。如果打算使用Gcov工具,编译代码时应该取消编译器优化选项,因为编译器优化通常组合多行代码到单个函数中,可能无法给出足够的信息以便于快速定位到占用了大量计算时间的“热点”代码。

使用Gcov时需使用两个特殊的GCC选项-fprofile-arcs和-ftest-coverage对源程序进行编译,它们用来告诉编译器产生Gcov所需的附加信息和文件。运行编译后的可执行程序时,将在目标文件路径中生成相应的gcda文件。

仍以前面的fastDBCppUintTest.cpp为例,执行覆盖分析后得到的过程结果信息如下:

打开Gcov工具生成的覆盖信息文件,其结果概况如下:

查阅Gcov在线帮助文档,理解和掌握Gcov的运行参数以及结果显示方式,在此基础上根据测试结果和覆盖情况对测试用例进行修改完善以达到规定的覆盖率要求,在线帮助文件的网址为https://gcc.gnu.org/onlinedocs/gcc/Gcov.html。

3)测试结果输出格式定制

CppUnit测试框架提供了Outputter抽象类用于输出测试结果汇总信息,具体实现了编译器兼容方式、纯文本方式以及XML文件方式的结果输出,涉及的类信息如图5-6所示。

图5-6 Outputter类的继承关系图

请分别采取三种方式输出测试结果,并基于形成的测试结果文件提交基于CppUnit的单元测试报告和学习心得。

4)复杂场景下Mock技术研究

从前面的实验过程中可以知道,CppUnit对于简单类的方法测试较为便利,但是对于复杂情况(例如被测类的依赖关系复杂,存在多继承、多态等情况)的支撑能力则较弱。针对这些复杂对象的测试,需要模拟那些不容易构造或者比较复杂的对象,这时就要借助于Mock技术进行测试。请查阅Mock方法的原理,并使用流行的Mock工具(如Google Mock)对FastDB项目中的复杂类进行测试,从而熟练掌握面向对象单元测试方法。 LA46Lo/qiU10+1EYJoGY5atvfPv3DOsCZZqVZYWT4GSxeKQGkP5EeMK9G8LVg4p3

点击中间区域
呼出菜单
上一章
目录
下一章
×