[vs2010 project] CppUnit快速入门

简介

测试是软件开发过程中极其重要的一环,详尽周密的测试能够减少软件BUG,提高软件品质。测试包括单元测试、系统测试等。其中单元测试是指针对软件功能单元所作的测试,这里的功能单元可以是一个类的属性或者方法,测试的目的是看这些基本单元是否工作正常。由于单元测试的内容很基础,因此可以看作是测试工作的第一环,该项工作一般由开发人员自行完成。如果条件允许,单元测试代码的开发应与程序代码的开发同步进行。

虽然不同程序的单元测试代码不尽相同,但测试代码的框架却非常相似,于是便出现了一些单元测试类库,CppUnit便是其中之一。

CppUnit是XUnit中的一员,XUnit是一个大家族,还包括JUnit和PythonUnit等。CppUnit简单实用,学习和使用起来都很方便,网上已有一些文章对其作介绍,但本文更着重于讲解其中的基本概念和使用方法,以帮助初次接触CppUnit的人员快速入门。

安装

1、  先下个最新版cppunit-1.12.1.tar.gz 解压缩,进入cppunit-1.12.1\src目录,就是源代码所在,打开CppUnitLibraries.dsw工程,是用vc6.0写的,用vs2010转换到CppUnitLibraries.sln ok,

2、  然后依次运行CppUnitLibraries.dsw工程下的每个项目,这样做的目的是为了方面发现问题和找到正确的解决方法。下面是我在运行相应项目时所提示的错误以及解决办法。未贴图,把下述的解决办法都做完即可,不需要查看贴图的错误提示

1)       运行项目Cppunit

解决办法:

选择Cppunit右键属性 ->(debug)配置属性->常规->目标文件名:$(ProjectName)修改成cppunitd(这样做是为保持链接器->常规->目标文件名 一致);

2)       运行项目cppunit_dll

 解决办法:

    选择Cppunit右键属性->(debug)配置属性->常规->目标文件名:$(ProjectName)修改成cppunitd_dll(这样做是为保持库管理器->常规->目标文件名 一致);

3)到这里这里会发现其实每个项目的错误基本上都是TargeName(xxx)LinkerOutputFile属性值不匹配;依次修改项目DllPlugInTesterDSPlugInTestPlugInRunnerTestRunnerDebug配置属性(每个后面都记得加个”d”,而且库管理器中为:目标文件名.xxxx不需要修改,但库管理器中的目标文件名可能要修改,该成与常规目标文件名一致!)

4)当然当修改完DSPlugIn的Debug配置属性后再运行我们发现

             我们仔细观察到底新的错误是什么呢?           这里修改方式就是最上面的红色字中提到的

修改TestRunnerUserInterface\DynamicWindow\MsDevCallerListCtrl.cpp文件第67行,改成:#import"libid:80cc9f66-e7d8-4ddd-85b6-d9e6cd0e93e2" version("8.0")lcid("0") raw_interfaces_only named_guids

5)再次运行DSPlugIn项目我们会发现依然有错误

解决方法:

选择项目->属性->配置属性->链接器->高级->无入口点 选择"是(/NOENTRY)"

6)最后为了生成全面的库文件我需要分别在Debug、release、Debug unicode、release unicode四种配置属性中生成全部解决方案。(运行过程中会遇到错误基本上都可以从上文中找到解决方法)

7)编译完成后,提示成功6  失败 0 即安装完毕

**************************************************************************************************
详细说明:

解压后,你可以看到CppUnit包含如下目录:

    config:  配置文件contrib: contribution,其他人贡献的外围代码doc:     文档,需要通过doxygen工具生成,也可以直接从sourceforge站点上下载打包好的文档examples:示例代码include: 头文件lib:     存放编译好的库src:     源文件,以及编译库的工程等

然后打开src目录下的CppUnitLibraries工程,执行build/batch build,编译成功的话,生成的库文件将被拷贝到lib目录下。

你也可以根据需要选择所需的项目进行编译,其中项目cppunit为静态库,cppunit_dll为动态库,生成的库文件为:

    cppunit.lib:     静态库release版cppunitd.lib:    静态库debug版cppunit_dll.lib: 动态库release版cppunitd_dll.lib:动态库debug版

要使用CppUnit,还得设置好头文件和库文件路径,以VC6为例,选择Tools/Options/Directories,在Include files和Library files中分别添加%CppUnitPath%/include和%CppUnitPath%/lib,其中%CppUnitPath%表示CppUnit所在路径。

做好准备工作后,我们就可以编写自己的单元测试代码了。需说明的是,CppUnit所用的动态运行期库均为多线程动态库,因此你的单元测试程序也得使用相应设置,否则会发生冲突。

概念

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

CppUnit核心内容主要包括六个方面,

1. 测试对象(Test,TestFixture,...):用于开发测试用例,以及对测试用例进行组织管理。

2. 测试结果(TestResult):                 处理测试用例执行结果。TestResult与下面的TestListener采用的是观察者模式(Observer Pattern)。

3. 测试结果监听者(TestListener):   TestListener作为TestResult的观察者,担任实际的结果处理角色。

4. 结果输出(Outputter):                  将结果进行输出,可以制定不同的输出格式。

5. 对象工厂(TestFactory):              用于创建测试对象,对测试用例进行自动化管理。

6. 测试执行体(TestRunner):           用于运行一个测试。

以上各模块的主要类继承结构如下:

         Test              TestFixture      TestResult          TestListener     _______|_________            |                                    |          |               |            |                           TestSuccessListenerTestComposite   TestLeaf         |                                    |          |               |____________|                           TestResultCollector          TestSuit                  |TestCase                     |TestCaller<Fixture>Outputter                                    TestFactory                    TestRunner____________________|_________________                            ||                   |                |                   TestFactoryRegistryCompilerOutputter  TextOutputter    XmlOutputter                      |TestSuiteFactory<TestCaseType>

接下来再对其中一些关键类作以介绍。

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),从而形成递归的树形结构。

TestCaller:TestCase适配器(Adapter),它将成员函数转换成测试用例

虽然我们可以从TestCase派生自己的测试类,但从TestCase类的定义可以看出,它只能支持一个测试用例,这对于测试代码的组织和维护很不方便,尤其是那些有共同上下文环境的一组测试。为此,CppUnit提供了TestCaller以解决这个问题。

TestCaller是一个模板类,它以实现了TestFixture接口的类为模板参数,将目标类中某个符合runTest原型的测试方法适配成TestCase的子类。

在实际应用中,我们大多采用TestFixture和TestCaller相组合的方式,具体例子参见后文。

TestResult和TestListener:处理测试信息和结果

前面已经提到,TestResult和TestListener采用了观察者模式,TestResult维护一个注册表,用于管理向其登记过的TestListener,当TestResult收到测试对象(Test)的测试信息时,再一一分发给它所管辖的TestListener。这一设计有助于实现对同一测试的多种处理方式。

TestFactory:测试工厂

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

TestRunner:用于执行测试用例

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

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

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

CppUnit在vs2010中配置:


参考:Vs2010下使用CppUint初体验

http://blog.csdn.net/leer168/article/details/6708732


vs2010代码实例:

CppUnitTest1

先让我们看看一个简单的例子:

#include <cppunit/TestCase.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>

// 定义测试用例
class SimpleTest : public CppUnit::TestCase
{
public:
    void runTest() // 重载测试方法
    {
        int i = 1;
        CPPUNIT_ASSERT_EQUAL(0, i);
    }
};

int main(int argc, char* argv[])
{
    CppUnit::TestResult r; 
    CppUnit::TestResultCollector rc;
    r.addListener(&rc); // 准备好结果收集器 

    SimpleTest t;
    t.run(&r); // 运行测试用例

    CppUnit::TextOutputter o(&rc, std::cout);
    o.write(); // 将结果输出

    return 0;
}
编译后运行,输出结果为:
!!!FAILURES!!!
Test Results:
Run: 1 Failures: 1 Errors: 0

1) test: (F) line: 18 E:/CppUnitExamples/SimpleTest.cpp
equality assertion failed
- Expected: 1
- Actual : 0

上面的例子很简单,需说明的是CPPUNIT_ASSERT_EQUAL宏。CppUnit定义了一组宏用于检测错误,CPPUNIT_ASSERT_EQUAL是其中之一,当断言失败时,CppUnit便会将错误信息报告给TestResult。这些宏定义的说明如下:

CPPUNIT_ASSERT(condition):判断condition的值是否为真,如果为假则生成错误信息。

CPPUNIT_ASSERT_MESSAGE(message, condition):与CPPUNIT_ASSERT类似,但结果为假时报告messsage信息。

CPPUNIT_FAIL(message):直接报告messsage错误信息。

CPPUNIT_ASSERT_EQUAL(expected, actual):判断expected和actual的值是否相等,如果不等输出错误信息。

CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual):与CPPUNIT_ASSERT_EQUAL类似,但断言失败时输出message信息。

CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta):判断expected与actual的偏差是否小于delta,用于浮点数比较。

CPPUNIT_ASSERT_THROW(expression, ExceptionType):判断执行表达式expression后是否抛出ExceptionType异常。

CPPUNIT_ASSERT_NO_THROW(expression):断言执行表达式expression后无异常抛出。


CppUnitTest2

接下来再看看TestFixture和TestCaller的组合使用:

#include <cppunit/TestCase.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>
#include <cppunit/TestCaller.h>
#include <cppunit/TestRunner.h>

// 定义测试类
class StringTest : public CppUnit::TestFixture
{
public:
    void setUp() // 初始化
    {
        m_str1 = "Hello, world";
        m_str2 = "Hi, cppunit";
    }

    void tearDown() // 清理
    {
    }

    void testSwap() // 测试方法1
    {
        std::string str1 = m_str1;
        std::string str2 = m_str2;
        m_str1.swap(m_str2);
        
        CPPUNIT_ASSERT(m_str1 == str2);
        CPPUNIT_ASSERT(m_str2 == str1);
    }

    void testFind() // 测试方法2
    {
        int pos1 = m_str1.find(',');
        int pos2 = m_str2.rfind(',');

        CPPUNIT_ASSERT_EQUAL(5, pos1);
        CPPUNIT_ASSERT_EQUAL(2, pos2);
    }

protected:
    std::string     m_str1;
    std::string     m_str2;
};

int main(int argc, char* argv[])
{
    CppUnit::TestResult r; 
    CppUnit::TestResultCollector rc;
    r.addListener(&rc); // 准备好结果收集器 

    CppUnit::TestRunner runner; // 定义执行实体
    runner.addTest(new CppUnit::TestCaller<StringTest>("testSwap", &StringTest::testSwap)); // 构建测试用例1
    runner.addTest(new CppUnit::TestCaller<StringTest>("testFind", &StringTest::testFind)); // 构建测试用例2
    runner.run(r); // 运行测试

    CppUnit::TextOutputter o(&rc, std::cout);
    o.write(); // 将结果输出

    return rc.wasSuccessful() ? 0 : -1;
}
编译后运行结果为:
OK (2 tests)

CppUnitTest3

上面的代码从功能上讲没有什么问题,但编写起来太繁琐了,为此,我们可以借助CppUnit定义的一套辅助宏,将测试用例的定义和注册变得自动化。上面的代码改造后如下:

#include <cppunit/TestResult.h>
#include <cppunit/TestResultCollector.h>
#include <cppunit/TextOutputter.h>
#include <cppunit/TestRunner.h>
#include <cppunit/extensions/HelperMacros.h>


// 定义测试类
class StringTest : public CppUnit::TestFixture
{
    CPPUNIT_TEST_SUITE(StringTest);  // 定义测试包
    CPPUNIT_TEST(testSwap);  // 添加测试用例1
    CPPUNIT_TEST(testFind);  // 添加测试用例2
    CPPUNIT_TEST_SUITE_END();  // 结束测试包定义
    
public:
    void setUp() // 初始化
    {
        m_str1 = "Hello, world";
        m_str2 = "Hi, cppunit";
    }

    void tearDown() // 清理
    {
    }

    void testSwap() // 测试方法1
    {
        std::string str1 = m_str1;
        std::string str2 = m_str2;
        m_str1.swap(m_str2);
        
        CPPUNIT_ASSERT(m_str1 == str2);
        CPPUNIT_ASSERT(m_str2 == str1);
    }

    void testFind() // 测试方法2
    {
        int pos1 = m_str1.find(',');
        int pos2 = m_str2.rfind(',');

        CPPUNIT_ASSERT_EQUAL(5, pos1);
        CPPUNIT_ASSERT_EQUAL(2, pos2);
    }

protected:
    std::string     m_str1;
    std::string     m_str2;
};

CPPUNIT_TEST_SUITE_REGISTRATION(StringTest); // 自动注册测试包

int main(int argc, char* argv[])
{
    CppUnit::TestResult r; 
    CppUnit::TestResultCollector rc;
    r.addListener(&rc); // 准备好结果收集器 

    CppUnit::TestRunner runner; // 定义执行实体
    runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
    runner.run(r); // 运行测试

    CppUnit::TextOutputter o(&rc, std::cout);
    o.write(); // 将结果输出

    return rc.wasSuccessful() ? 0 : -1;
}

CppUnit的简单介绍就到此,相信你已经了解了其中的基本概念,也能够开发单元测试代码了。

其它CppUnit还包括其它一些辅助模块,比如基于MFC的图形化测试界面,下面这篇文章对此有所介绍:

     CppUnit测试框架入门

 

CppUnit使用了很多设计模式,整体构架还算清晰合理,源码也比较简单易懂,这对于学习设计模式是一个不错的选择。网上已有这样的一些资料:

     CppUnit源码解读       CppUnit代码简介 - 第一部分,核心类

(freefalcon于2006-05-22)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/458694.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[javascript|基本概念|Number]学习笔记

Number类型的值&#xff1a;整数/浮点数值 整数 十进制 e.g.: var intNum 50; 八进制 (严格模式下无效,解析错误)字面值首位必须是0,之后的数字序列为0&#xff5e;7 e.g.: var intNum 070; //解析为十进制56 (如果字面值数值超出了范围&#xff0c;前导0将被忽略&#xf…

[转]深入理解linux内核list_head

http://blog.chinaunix.net/uid-27122224-id-3277511.html 深入理解linux内核list_head的实现 2012-07-17 17:37:01 分类&#xff1a; LINUX 前言&#xff1a;在linux源代码中有个头文件为list.h。很多linux下的源代码都会使用这个头文件&#xff0c;它里面定义 了一个结构,以及…

xcode左侧不显示工程文件目录,提示NO Filter Results

解决办法&#xff1a; What solved was to go to Navigate > Reveal in Project Navigator . After this, the structure appeared again.

【VC++技术杂谈005】如何与程控仪器通过GPIB接口进行通信

在工控测试系统中&#xff0c;经常需要使用到各类程控仪器&#xff0c;这些程控仪器通常具有GPIB、LAN、USB等硬件接口&#xff0c;计算机通过这些接口能够与其通信&#xff0c;从而实现自动测量、数据采集、数据分析和数据处理等操作。本文主要介绍如何与程控仪器通过GPIB接口…

标题在上边框中的html(fieldset标签)

<fieldset> <legend>标题</legend> 内容 </fieldset> 转载于:https://www.cnblogs.com/lswbk/p/4952820.html

移除项目中的CocoaPods

在项目中移除CocoaPods cocoaPods虽然很方便&#xff0c;但是我是真心的不喜欢用它&#xff0c;总是出错如果你觉得CocoaPods让你的项目出现了问题&#xff0c;不好用甚至是恶心&#xff0c;想将其从项目中彻底移除&#xff0c;也有方法&#xff1a; 1.删除工程文件夹下的Podf…

ShellExecute使用详解

有三个API函数可以运行可执行文件WinExec、ShellExecute和CreateProcess。 1.CreateProcess因为使用复杂&#xff0c;比较少用。 2.WinExec主要运行EXE文件。如&#xff1a;WinExec(Notepad.exe Readme.txt, SW_SHOW); 3.ShellExecute不仅可以运行EXE文件&#xff0c;也可以运行…

javascript笔记整理(对象基础)

一、名词解释 1.基于对象&#xff08;一切皆对象&#xff0c;以对象的概念来编程&#xff09; 2.面向对象编程(Object Oriented Programming&#xff0c;OOP) A.对象(JavaScript 中的所有事物都是对象) B.对象的属性和行为 属性:用数据值来描述他的状态 行为:用来改变对象行为的…

java的安装和配置

JRE (JAVA Runtime Enviroment java运行环境),包括JVM(java虚拟机)和java程序所需的核心功能类库&#xff0c;如果只是运行java程序&#xff0c;只需安装JRE。 JDK &#xff08;Java Development Kit 开发工具包&#xff09;包括开发JAVA程序时所需的工具&#xff0c;包括JRE…

#if, #ifdef, #ifndef, #else, #elif, #endif的用法

#ifdef的用法 灵活使用#ifdef指示符&#xff0c;我们可以区隔一些与特定头文件、程序库和其他文件版本有关的代码。 代码举例&#xff1a;新建define.cpp文件 &#xff03;include "iostream.h" int main() { #ifdef DEBUG cout<< "Beginning ex…

redhat 6.6 安装 (LVM)

http://www.cnblogs.com/kerrycode/p/4341960.html转载于:https://www.cnblogs.com/zengkefu/p/4954955.html

MFC对话框最小化到托盘

1、在资源中的Icon中导入一个自己喜欢的图标&#xff0c;ID命名为IDR_MAINFRAME&#xff0c;将先前的IDR_MAINFRAME的图标删除掉&#xff1b; 2、在自己的Dialog头文件中定义一个变量 NOTIFYICONDATA m_nid&#xff0c;关于该结构体的具体信息可以查阅MSDN&#xff1b; 3、添加…

Android acache读后感

今天了解到了一个android轻量级的开源缓存框架,(github&#xff1a;https://github.com/yangfuhai/ASimpleCache),花了一点时间研究了一下源代码&#xff0c;大概的思路就是每个缓存目录对应一个Acache类&#xff0c;通过mInstanceMap关联&#xff08;个人觉得这个主要是减少对…

continue break

块作用域 一个块或复合语句是用一对花括号&#xff08;"{}"&#xff09;括起来的任意数量的简单的java语句。块定义了变量的作用范围。 1、嵌套块是方法内的嵌套&#xff0c;不包括类的花括号。在嵌套块内的 变量是不可以重复定义的。 2、不允许重复定义的是局部变…

GetVersionEx 获取系统版本信息

转自&#xff1a;http://blog.csdn.net/yyingwei/article/details/8286658 最近在windows 8上获取系统版本信息需要调用系统API&#xff0c;于是用到了GetVersionEx。 首先看一看函数原型&#xff1a; [cpp] view plaincopy BOOL GetVersionEx(POSVERSIONINFO pVersionInformat…

popoverController(iPad)

一、设置尺寸 提示&#xff1a;不建议&#xff0c;像下面这样吧popover的宽度和高度写死。 1 //1.新建一个内容控制器2 YYMenuViewController *menuVc[[YYMenuViewController alloc]init];3 4 //2.新建一个popoverController&#xff0c;并设置其内容控制器5 s…

静态成员变量和非静态成员变量的对比

静态成员变量和非静态成员变量的对比 1、存储的数据 静态成员变量存储的是所有对象共享的数据 非静态成员变量存储的是每个对象特有的数据 2、存储位置 静态成员变量是随着类的加载在方法区的静态区开辟内存了 非静态成员变量是随着对象的创建再堆中开辟内存 3、调用方式 静态成…

c++的thread类(c++线程简单用法)

最近看了一个Thread类&#xff08;忘记在哪里看的了&#xff09;&#xff0c;感觉不错。 创建线程时线程对应的函数必须是类的静态成员&#xff0c;由于静态成员无法访问类的非静态成员&#xff0c;我从前都是把对象的指针作为参数传递给线程函数来避免这个问题&#xff0c;但是…

[LeetCode]Merge Sorted Array

题目描述:(链接) Given two sorted integer arrays nums1 and nums2, merge nums2 into nums1 as one sorted array. Note:You may assume that nums1 has enough space (size that is greater or equal to m n) to hold additional elements from nums2. The number of eleme…

[LeetCode]Integer to Roman

题目描述:(链接&#xff09; Given an integer, convert it to a roman numeral. Input is guaranteed to be within the range from 1 to 3999. 解题思路&#xff1a; 1 class Solution {2 public:3 string intToRoman(int num) {4 vector<int> values{1000…