一种基于Perl的单元测试装置及方法
技术领域
本发明涉及一种独立于开发环境,编译器的面向C++的单元测试装置及方法。
背景技术
对软件进行持续的测试,持续关注质量是软件开发的关键原则和实践。通过分析错误产生的原因和错误的发生趋势,可以帮助项目管理者发现当前软件开发过程中的缺陷,以便及时改进。这种分析也能帮助测试人员设计出有针对性的测试方法,改善测试的效率和有效性,目前的软件开发都是强调以迭代开发的方式,减少风险,提高软件质量,迭代开发的主要优点之一是使团队能够尽早地、持续地进行测试,持续对软件质量进行验证。逐步建立测试自动化,是实现软件质量保证的有效方法。自动测试的目的是使所有代码单元测试以及部分验收测试自动化。自动测试的基础工程活动是使用单元测试装置,单元测试是在软件开发过程中进行的最低级别的测试活动,在单元测试活动中,软件的独立单元将在与程序的其他部分相隔离的情况下进行测试,对于C++而言,每个类接口或函数过程都必须有测试用例函数进行追踪和覆盖,并使用单元测试装置将所有的测试用例函数进行管理和协调封装,单元测试装置的好坏直接关系到是否能成功实践自动化测试。采用一套优秀的单元测试装置是保证软件开发质量的基础工程实践之一。
C++项目的一个特点是多样性。因为C++不像.net和java那样有统一的虚拟机框架,C++的编译器、平台和编程方式多种多样,导致在C++下制作和使用一款通用的单元测试装置并不容易。如果想使用测试驱动开发(TDD),会频繁的编写和运行一些小测试,对框架的轻便性会有要求,而针对一些特殊项目,如游戏开发,测试会需要运行在不同的平台(PC,XBOX,PS3)上,对跨平台性就会有要求,这些都是测试框架必须考虑到的。
C++面向的项目更多在独立的产品,设备和高内聚的模块,而不是耦合度很高的软件服务,因此,面向C++的单元测试装置必然和面向网站服务的单元测试装置侧重点不同。面向C++的单元测试装置不需要很复杂的脚本驱动功能,单元测试用例使用本身的C++语言进行编写对于大多数C++项目已经足够,但是更加强调面向接口的单元测试功能,能够方便的组织这些单元测试合并成测试集,并且需要考虑跨平台的支持功能,以便在推出不同产品系列的时候,原有的单元测试代码可以继续复用,保护项目投资。
单元测试装置应该有这样的特性:轻便和简洁的加入新测试,除了编写本身的测试用例函数外,几乎不需要其他工作就可以使测试用例函数加入到最终的测试集合中运行,如果经常进行单元测试,重复的敲入一些代码容易出错并且效率低,尤其在使用TDD时,越简单越短小,就越容易重构;容易修改和移植,不应该和一些特殊的特性相关(RTTI,异常处理等);能够支持测试套件和针对套件的初始和清理操作;能够处理异常,而不是某些代码出错就导致整个测试中断退出;能够很好的断言,并且提供多种类型的条件断言函数;能够支持多种输出方式,可以在IDE中显示测试结果,以及以图形或控制台及文本方式显示测试结果。
发明内容
本发明的目的提供种基于Perl的单元测试装置及测试方法。该装置能以最小的工作量添加新的测试,使用动态脚本语言Perl扫描C++源代码提取特征代码,自动创建测试描述源代码,而不依赖于编译器提供的高级机制。
一种基于Perl的单元测试装置,包括源码解析模块、测试套件模块、测试描述模块、测试断言模块、测试监听模块、测试运行模块、测试输出模块,其特征在于:测试套件模块与源码解析模块相连,源码解析模块分别与测试描述模块、测试断言模块、测试运行模块相连,测试运行模块与测试监听模块相连,测试监听模块与测试输出模块相连,其中,所述的源码解析模块用于分析输入的C++测试类,提取其成员函数;所述的测试套件模块提供测试类需要使用的接口,所有的测试类都需要继承此基类;所述的测试描述模块对测试函数进行封装的,所有的测试函数都会被自动封装成此函数的子类,由测试运行模块调用;所述的测试断言模块用于将测试结果发送到监听模块;所述的测试监听模块用于记录测试结果;所述的测试运行模块,用于协调各个测试描述模块的运行;所述的测试输出模块用于以不同方式输出测试结果。
一种基于Perl的单元测试方法,其特征在于:包括如下步骤:
(1)编写测试类,并将测试类定义为测试套件类的子类;
(2)编写测试函数作为测试类的成员函数,并以CPT_开头命名;
(3)在测试函数中加入断言以确定测试成功或失败;
(4)在测试类中加入SetUP和TeadDown函数来进行初始化操作和销毁操作;
(5)使用Perl脚本的源码解析模块对编写的测试类进行处理,生成最终测试描述文件并确定输出方式;
(6)调用编译器编译测试描述文件,生成可执行文件;
(7)运行生成的可执行文件,得到测试结果,并以各种方式发送测试运行结果。
所述的第(5)具体步骤分为两个阶段:分析过程和写入过程;
分析过程:首先,得到测试文件列表和输出显示方法参数,然后从测试文件列表中取一个文件,从头开始分析文件的每一行,当发现如下几种情况时,做相应处理:
●发现std关键字:就标记此文件使用标准函数库;
●发现异常处理代码片段:就标记此文件使用了异常处理;
●发现测试类代码开头的特征片段代码:提取测试类名及相应信息;
●发现以CPT_开头的类成员函数:就提取成员函数的名称及相应信息;
●发现测试类代码结束的特征片段代码:标记测试类信息;
以此步骤不断地遍历文件的每一行,提取所需要的信息,当一个文件遍历完后,就找出下一个测试文件,直到测试文件列表中所有文件都被扫描,然后就开始写入测试描述文件的过程;
写入过程中写入测试描述文件分为以下几个步骤:(1)根据是否使用异常处理机标准模块库信息,确定包含的头文件以及一些预定义;(2)写入main函数代码,进行静态变量的初始化工作,并在其中加入TestRunner<输出方式>.run()的代码;(3)遍历在分析过程得到的所有测试类,输出代码;(4)对分析过程得到每个测试函数,封装为测试描述类的子类,并直接生成该类的一个对象,输出的代码;(5)将测试描述类的子类代码,以源文件的形式输出。
本发明提供的单元测试装置的特点在于提出了一种运行机制,这个运行机制包含了很多类对象的协作:即测试套件类、测试描述类、测试运行类、测试监听类、测试输出类之间的协作,而它们需要的信息(例如,需要测试哪些函数)来自于提供的源码测试文件,这些信息的提取不是人工,而是以程序扫描的方式获得。不像其他的一些装置需要手动编写脚本或者配置文件或者源代码来指明测试函数的映射关系。
本发明提供的单元测试装置的特点在于不依赖C++的运行时类型识别也不依赖外部函数库。方法为:使用动态脚本语言Perl丰富的字符串匹配和正则表达式功能而不是使用C++的反射机制提取源代码中的特征代码,并对特征代码进行提炼和替换,并重新组装到新的测试文件源码中。目前的C++单元测试装置都需要反射机制来提取源代码中的测试函数,反射机制属于C++的高级机制,不是编译器通用支持的。例如VC和VS.NET 2002支持的不同,VS.NET 2003以上或者Intel C++Complier,G++对这些高级特性支持也不同。使用本装置可以方便的适应各种C++编译器,甚至可以升级为跨语言的测试装置。
本发明提供的单元测试装置的特点在于不需要手动注册测试用例函数,自动创建测试运行类,方法为:以写好的C++源码测试文件作为输入,使用Perl脚本扫描测试文件,自动输出C++格式的测试描述源代码,不用手动注册测试用例函数,只需要测试用例函数的命名采用约定的CPT_开头,并且测试类继承与已约定好接口的测试套件基类,然后用编好的Perl脚本扫描测试用例函数所在的文件,即可提取需要的函数。
本发明提供的单元测试装置的特点在于,对于C++的模板函数的测试和调试具有良好的支持。不需要特意为使用某种特性而调整编译器的运行时库属性,因为不依赖C++的反射功能,而是使用正则表达式的字符串匹配,所以本发明提供的单元测试装置可以在测试类中直接编写模板方法,而不用像其他装置需要做迎合性的改变。
附图说明
图1为本发明的系统框图和其模块之间的交互。
图2为本发明实施例中编写测试描述的流程图。
图3为本发明实施例中生成及运行最终测试的流程图。
图4为本发明源码分析模块的实现流程图。
具体实施方式
为了使本发明的目的,方案和优点更加清楚,下面将结合附图和具体实施对本发明做进一步详细描述。
图1表示了本发明的装置的结构图和其模块之间的交互。本发明的装置的模块及其输入输出文档的定义如下:
测试套件模块M01:是一组装置内预先定义好的类和函数。其中的TestSuite类是所有编写的测试类必须继承的基类,在编写测试之前,先要编写一个测试类,并继承于TestSuite类,每个测试类代表一个模块测试,测试类中的测试成员函数代表一个接口或单元测试。所有的测试类组合成测试类源文件D01。
源码解析模块M02:运行在Perl环境S10下的脚本,通过正则表达式和字符串处理,将输入的测试类源文件中那些以CPT_开头的函数,提取出来,封装到单独的测试描述类中,这些测试描述类都是TestDescriptor类的子类。
测试描述模块M03:主要包含TestDescriptor类,所有的测试函数都会被自动封装成这个类的子类。测试描述类封装了一些方法,包括:寻找下一个测试描述类对象和运行本测试等基本方法,方便测试运行模块调用。
测试断言模块M04:用于将测试结果发送到监听模块。测试断言模块提供了很多断言函数,比如CPT_ASSERT、CPS_FAIL,CPS_EAQUAL等。通过这些断言函数,可以取得测试成功或失败的相关信息,用于帮助定位问题所在。
通过源码解析模块M02,将符合M01,M02,M03,M04规范的源代码组合成测试描述类源文件并在其中加入测试运行类TestRunner,测试运行类TestRunner是在测试运行模块M05中定义的,它是最终测试源码的运行入口,以TestRunner<Win32Gui,PrinterType>.run()的形式被main函数调用。在TestRunner的run成员函数中,将会运行所有的测试函数。
测试描述类源文件将会在特定的C++编译器环境S11下编译,生成最终的可执行程序。
运行生成的可执行程序,测试监听模块M06会监听任何调用断言的测试,更新测试结果,并将最终测试结果发送到输出模块M07。
测试输出模块M07:用于将测试结果输出,可以预先选择测试输出的方式,使用gui,console或者文件,根据使用输出方式的不同,最后的结果会以不同方式呈现。
图2,图3表示本发明方法进行的步骤。
第一步,编写测试类,并从TestSuite继承,将此文件存为AlgorithmTest.h,代码如下所示:
第二步,在其中加入测试函数。例如一个冒泡排序法的测试函数,使用断言函数CPT_ASSERT来指示测试成功或失败。
第三步,使用Setup和Teaddown函数对测试函数进行统一的初始化和结束处理。
第四步,使用命令行“perl CptGenerator.pl输出显示方式测试文件列表测试描述文件”的形式输出测试描述文件。CptGenerator.pl就是源码解析模块的perl脚本,其实现方式如图4所示。分为两个阶段:分析过程和写入过程。
分析过程:
首先,得到测试文件列表和输出显示方法等参数,然后从测试文件列表中取一个文件,从头开始分析文件的每一行,当发现如下几种情况时,做相应处理:
●发现std关键字:就标记此文件使用标准函数库;
●发现异常处理代码片段:就标记此文件使用了异常处理;
●发现测试类代码开头的特征片段代码:提取测试类名及相应信息;
●发现以CPT_开头的类成员函数:就提取成员函数的名称及相应信息;
●发现测试类代码结束的特征片段代码:标记测试类信息。
以此步骤不断地遍历文件的每一行,提取所需要的信息,当一个文件遍历完后,就找出下一个测试文件,直到测试文件列表中所有文件都被扫描,然后就开始写入测试描述文件的过程。
写入过程:
写入测试描述文件分为以下几个步骤:
1.根据是否使用异常处理机标准模块库等信息,确定包含的头文件以及一些预定义;
2.写入main函数代码,进行静态变量的初始化工作,并在其中加入
TestRunner<输出方式>.run()的代码;
3.遍历在分析过程得到的所有测试类,输出如下代码:
static AlgorithmTest suite_AlgorithmTest;
4.对分析过程得到每个测试函数,封装为测试描述类的子类,并直接生成该类的一个对象。输出的代码示例如下:
5.将测试描述类的子类代码,以源文件的形式输出,本例中,输出文件为test.cpp。
源码解析模块的功能全部是由perl脚本完成的,也就是说以上所有的测试描述子类的代码不是手工输入的,而是由perl分析后自动输出的,省去了大量的手工重复的时间,减少了出错几率,使得测试函数的编写变得十分轻量和简便,极大的提高了效率。
第五步,使用编译器对输出的测试描述文件进行编译,以VC为例,使用“cl.exe-GX-W3-WX-I.-I..-o tester.exe test.cpp user.lib”,输出为tester.exe的可运行文件。
第六步,运行生成的tester.exe,就可以输出测试运行结果。
以上的测试描述文件的生成和编译过程以及执行过程都可以嵌入C++通用的MAKEFILE文件中,让这几个步骤变得自动化。也可以将上述几个过程放入另外的构建装置中,实现敏捷过程提倡的每日构建和测试,
以上所述的装置中,测试套件的组织是动态的,只需要按命名规则新增加测试函数即可将新的测试用例加入到最终的测试描述文件中,这一些都是动态的不需要像其他装置那样显示的手工注册测试用例。当不需要某个测试用例时,只需要简单的去掉或者重命名测试函数,在最终的测试描述文件会自动去除掉这个测试用例,不需要其他手工的去除工作,这样在反复修改或编写测试用例时,会极大提高了测试效率和减少出错机会,尤其在构建自动的测试环境时,优势会十分明显。只需要简单的命令行就可以按照测试类中的函数生成最终测试描述文件而不是硬编码其代码,采用这种方式和其他集成构建的工具或装置结合能够在较少的工时成本下实现每日自动构建测试,减少人工的介入,和人工成本,为项目质量提供稳固的保障。
以上结合实例对本发明作了说明,应指出,本领域技术人员可以做出各种形式的和细节上的改变,而不偏离由所附权利要求所确定的本发明的精神和范围。