以下内容来自:http://www.cnblogs.com/wishma/archive/2008/08/01/1258370.html

1. 简介

CppUnit 是个基于 LGPL 的开源项目,最初版本移植自 JUnit,是一个非常优秀的开源测试框架。CppUnit 和 JUnit 一样主要思想来源于极限编程(XProgramming)。主要功能就是对单元测试进行管理,并可进行自动化测试。这样描述可能没有让您体会到测试框架的 强大威力,那您在开发过程中遇到下列问题吗?如果答案是肯定的,就应该学习使用这种技术:

  • 测试代码没有很好地维护而废弃,再次需要测试时还需要重写;
  • 投入太多的精力,找 bug,而新的代码仍然会出现类似 bug;
  • 写完代码,心里没底,是否有大量 bug 等待自己;
  • 新修改的代码不知道是否影响其他部分代码;
  • 由于牵扯太多,导致不敢进行修改代码;

2. 基本思想

对于上面的问题仅仅说明 CppUnit 的使用是没有效果的,下面先从测试的目的,测试原则等方面简要说明,然后介绍 CppUnit 的具体使用。

首先要明确我们写测试代码的目的,就是验证代码的正确性或者调试 bug。这样写测试代码时就有了针对性,对那些容易出错的,易变的编写测试代码;而不用对每个细节,每个功能编写测试代码,当然除非有过量精力或者可靠性要求。

编 码和测试的关系是密不可分的,推荐的开发过程并不要等编写完所有或者很多的代码后再进行测试,而是在完成一部分代码,比如一个函数, 之后立刻编写测试代码进行验证。然后再写一些代码,再写测试。每次测试对所有以前的测试都进行一遍。这样做的优点就是,写完代码,也基本测试完一遍,心里 对代码有信心。而且在写新代码时不断地测试老代码,对其他部分代码的影响能够迅速发现、定位。不断编码测试的过程也就是对测试代码维护的过程,以便测试代 码一直是有效的。有了各个部分测试代码的保证,有了自动测试的机制,更改以前的代码没有什么顾虑了。在极限编程(一种软件开发思想)中,甚至强调先写测试 代码,然后编写符合测试代码的代码,进而完成整个软件。

根据上面说的目的、思想,下面总结一下平时开发过程中单元测试的原则:

  • 先写测试代码,然后编写符合测试的代码。至少做到完成部分代码后,完成对应的测试代码;
  • 测试代码不需要覆盖所有的细节,但应该对所有主要的功能和可能出错的地方有相应的测试用例;
  • 发现 bug,首先编写对应的测试用例,然后进行调试;
  • 不断总结出现 bug 的原因,对其他代码编写相应测试用例;
  • 每次编写完成代码,运行所有以前的测试用例,验证对以前代码影响,把这种影响尽早消除;
  • 不断维护测试代码,保证代码变动后通过所有测试;

有上面的理论做指导,测试行为就可以有规可循。那么 CppUnit 如何实现这种测试框架,帮助我们管理测试代码,完成自动测试的?下面就看看 CppUnit 的原理。

3. cppunit的基本原理

CppUint进行测试的基本原理是:对于每一批测试,编写一个继承自TestFixture的测试类,其中setUp和tearDown 两个虚函数分别用于进行测试数据的初始化和释放操作;对于每一项测试,分别定义一个void返回类型的无参数方法,检测测试过程,可以用以下几个宏:

CPPUNIT_ASSERT(condition)

断言condition为真,否则失败

CPPUNIT_ASSERT_MESSAGE(message,condition)

当condition为假时失败, 并打印message

CPPUNIT_FAIL(message)

当前测试失败, 并打印message

CPPUNIT_ASSERT_EQUAL(expected, actual)

确信两者相等,否则失败

CPPUNIT_ASSERT_EQUAL_MESSAGE(message, expected, actual)

确信两者相等,否则失败,并同时打印message

CPPUNIT_ASSERT_DOUBLES_EQUAL(expected, actual, delta)

当expected和actual之间差大于delta时失败

其中,“失败”的含义就是程序运行出现异常,并且所有测试过程中止。

在完成一个或者多个测试类的编写后,需要用TestRunner类的实例来运行每个测试类的测试函数进行测试。TestRunner类型共有:TextUi::TestRunner,QtUi::TestRunner,MfcUi::TestRunner三个,都可以用来运行测试,其区别为:

TextUi::TestRunner

默认将信息输出到控制台,也可以指定输出到std::ostringstream。

QtUi::TestRunner

用QT的GUI输出信息

MfcUi::TestRunner

用MFC的GUI输出信息

整个测试过程需要另外附加代码,所以通常情况下,测试用例代码和被测试对象因在不同的项目中。应该在另一个项目(最好在不同的目录)中编写测试类,然后把被测试的对象包含在测试项目中。根据测试工程建立的类型,Consol可以选择TextUi::TestRunner,将结果输出至控制台显示,MFC的GUI可以使用MfcUi::TestRunner,使用GUI来控制并查看测试结果。测试工程与被测试对象所在工程的类型是可以不同的。

在使用VC作为开发工工具时,建议使用TextUi::TestRunner,并更改其Outputter实例,使用附加的TraceOutputter,将信息输出到信息输出栏,双击信息输出栏中一行,即可定位到出错位置。

4. 建立cppunit测试实例的一般步骤

建立一个cppunit测试实例的一般步骤如下:

  1. 包含文件#include <cppunit/extensions/HelperMacros.h>
  2. 声明一个类,比如,MathTest,继承自CppUnit::TestFixture。
  3. 声明setUp和tearDown函数。
  4. 声明测试函数,比如testAdd
  5. 如果含有其他测试函数,继续添加。
  6. 在类声明部分添加如下宏,声明改类为测试类,并且声明那些函数为测试函数
CPPUNIT_TEST_SUITE( MathTest);  //声明一个TestSuite
CPPUNIT_TEST( testAdd );  //添加TestCase到TestSuite
/* 定义的TestCase需要在此声明。如果有其他TestCase,可以继续添加到此处 */
CPPUNIT_TEST_SUITE_END();  //TestSuite声明完成

7. 最后在实现的CPP文件中,添加下面的宏,用于注册测试类到某个测试项目,比如以"alltest"命名的项目。

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MathTest, "alltest" );

  8. 实现setUp和tearDown,以及各个测试函数。

6. cppunit的测试代码实例

需要测试的类名为Math,待测试的函数的Math::testAdd().

//Math.h
class Math{
    public:
    Math();
    ~Math();
    int add(int, int);
};

//Math.cc
#include "Math.h"
int Math::add(int x, int y){
    return x+y;
}

cppunit测试代码如下:

//MathTest.h
#include "cppunit/TestFixture.h"
#include "cppunit/extensions/HelperMacros.h"

class MathTest:public CppUnit::TestFixture{
    CPPUNIT_TEST_SUITE(MathTest);
    CPPUNIT_TEST(testAdd);
    CPPUNIT_TEST_SUITE_END();
protected:
    int m_value1, m_value2;

public:
    MathTest(){}

    void setUp();
    void tearDown();

    void testAdd();
};

//MathTest.cc
#include "MathTest.h"
#include "Math.h"
#include "cppunit/TestAssert.h"

CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(MathTest, "alltest");

void MathTest::setUp(){
    m_value1 = ;
    m_value2 = ;
}

void MathTest::tearDown(){
}

void MathTest::testAdd(){
    int ret = m_value1 + m_value2;
    CPPUNIT_ASSERT(ret == );
}

测试的主代码如下:

#include "Math.h"
#include "MathTest.h"
#include <iostream>
#include "cppunit/ui/text/TestRunner.h"
#include "cppunit/extensions/TestFactoryRegistry.h"

int main(void){
    CppUnit::TextUi::TestRunner runner;
    CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry("alltest");
    runner.addTest(registry.makeTest());
    runner.run();
}

编译:  g++ Math.cpp MathTest.cpp main.cpp -lcppunit -ldl -lm -lpthread -o mathTest

运行:  ./mathTest

显示:

通过的情况:OK (1 tests)

失败的情况:会显示那个测试类中的哪行测试不通过

!!!FAILURES!!!
Test Results:
Run: 1 Failures: 1 Errors: 0
1) test: MathTest::testAdd (F) line: 15 MathTest.cpp
assertion failed
- Expression: ret==4
05-04 08:04