CppUnit使用教程以及原理分析

1.     CppUnit用法简介

1.1.            基本用法

1.1.1.      使用TestCase类创建测试用例

如下代码所示:

//定义测试用例类

class SimpleTestCase:publicCPPUNIT_NS::TestCase

{

public:

    virtual voidrunTest()

    {

        int x = 3;

        int y = 6;

        CPPUNIT_ASSERT(x+y == 10);

    }

};

// 创建TestCase

CPPUNIT_NS::Test*makeSingleTestCase()

{

    return newSimpleTestCase();

}

1.1.2.      使用TestCaller创建测试用例

如下代码所示:

//定义TestFixture类

class SimpleTestFixture:publicCPPUNIT_NS::TestFixture

{

public:

    virtual voidsetUp()

    {

        x = 10;

        y = 6;

    }

    virtual voidtearDown()

    {

    }

    void  TestSub()

    {

        CPPUNIT_ASSERT(x-y == 3);

    }

protected:

    int x;

    int y;

};

//创建TestCaller

CPPUNIT_NS::Test*makeTestCallCase()

{

    SimpleTestFixture* fixture= new SimpleTestFixture;

    CPPUNIT_NS::TestCaller<SimpleTestFixture>*caller=

        new CPPUNIT_NS::TestCaller<SimpleTestFixture>("SimpleTestFixture",

        &SimpleTestFixture::TestSub,

        fixture);

    return caller;

}

1.1.3.      创建TestSuite

如下代码所示:

//创建TestFixture类

class SimpleTestSuiteFixture:publicCPPUNIT_NS::TestFixture

{

public:

    virtual voidsetUp()

    {

        x = 15;

        y = 5;

    }

    virtual voidtearDown()

    {

    }

    void TestMulti()

    {

        CPPUNIT_ASSERT(x*y == 65);

    }

    void TestDivide()

    {

        CPPUNIT_ASSERT(x/y == 4);

    }

 

protected:

    int x;

    int y;

};

//创建并添加TestSuite

CPPUNIT_NS::Test*makeTestSuite()

{

    CPPUNIT_NS::TestCaller<SimpleTestSuiteFixture>*caller1 =

        new CPPUNIT_NS::TestCaller<SimpleTestSuiteFixture>("SimpleTestSuiteFixture",

        &SimpleTestSuiteFixture::TestMulti,

        new SimpleTestSuiteFixture());

 

    CPPUNIT_NS::TestCaller<SimpleTestSuiteFixture>*caller2 =

        new CPPUNIT_NS::TestCaller<SimpleTestSuiteFixture>("SimpleTestSuiteFixture",

        &SimpleTestSuiteFixture::TestDivide,

        new SimpleTestSuiteFixture());

 

    CPPUNIT_NS::TestSuite*suite =new CPPUNIT_NS::TestSuite;

    suite->addTest(caller1);

    suite->addTest(caller2);

   

    return suite;

}

1.1.4.      运行测试用例

#include "cppunit/extensions/HelperMacros.h"

#include "cppunit/ui/text/TestRunner.h"

#include "simple_test.h"

int _tmain(intargc, _TCHAR*argv[])

{

    CppUnit::TextUi::TestRunnerrunner;   

 

    runner.addTest(makeSingleTestCase());

    runner.addTest(makeTestCallCase());

    runner.addTest(makeTestSuite());

 

    runner.run();

     return 0;

}

运行结果如图所示:


1.2.            进阶用法

通过上面的例子我们会发现编写测试用例的过程较为复杂,所以CppUnit提供了一些工具类来简化定义测试用例的流程,我们把上面的例子做一些改动。首先把所有的测试用例都放到一个TestSuite,再使用工具类的新写法来完成这个测试用例。

1.2.1.      定义测试类

#include "cppunit/extensions/HelperMacros.h"

 

class MathTest:public CPPUNIT_NS::TestFixture

{

    CPPUNIT_TEST_SUITE(MathTest);//定义TestSuite

 

    CPPUNIT_TEST(TestAdd);//添加TestCase

    CPPUNIT_TEST(TestSub);//添加TestCae

    CPPUNIT_TEST(TestMulti);//添加TestCase

    CPPUNIT_TEST(TestDivide);//添加TestCase

 

    CPPUNIT_TEST_SUITE_END(); //定义TestSuite结束

public:

    void  TestAdd();

    void  TestSub();

    void  TestMulti();

    void  TestDivide();

 

    virtual voidsetUp();

    virtual voidtearDown();

 

protected:

    int x;

    int y;

};

 

1.2.2.      实现测试类

CPPUNIT_TEST_SUITE_REGISTRATION(MathTest);//将TestSuite注册到工厂,该行非常重要,一定需要

 

void MathTest::TestAdd()

{

    CPPUNIT_ASSERT(x+y == 21);

}

 

void MathTest::TestSub()

{

    CPPUNIT_ASSERT(x-y == 16);

}

 

void MathTest::TestMulti()

{

    CPPUNIT_ASSERT(x*y == 100);

}

 

void MathTest::TestDivide()

{

    CPPUNIT_ASSERT(x/y == 3);

}

 

void MathTest::setUp()

{

    x = 15;

    y = 5;

}

 

void MathTest::tearDown()

{

}

1.2.3.      运行测试用例

#include "cppunit/extensions/TestFactoryRegistry.h"

#include "cppunit/ui/text/TestRunner.h"

 

int main(intargc, char *argv[])

{

    CppUnit::TextUi::TestRunner runner;

    CppUnit::TestFactoryRegistry&registry =

        CppUnit::TestFactoryRegistry::getRegistry();

    runner.addTest(registry.makeTest());

    runner.run();

    return 0;

}

运行结果如图所示:


 

2.     CppUnit框架剖析

2.1.            CppUnit Core

CppUnit的核心如图1所示:

图1 CppUnit Core 类图

主要由以下类构成:

1. Test       所有测试的基类。

2.TestSuite   表示一系列测试用例的集合,也可以包含其他TestSuite。

3.TestCase   表示一个单一的TestCase。

4.TestFixture 用于初始化和回收测试用例。

5.TestCaller  用于封装一个TestFixture成TestCase。

6.TestRunner 启动测试用例的主程序,可以通过addTest将需要运行的测试用例添加到单元测试程序中。

从上图我们可以看出,CppUnit的核心就是TestRunner运行所有添加到它里面的Test,而一个Test可以是一个具体的TestCase,也可以是一个TestSuite,非常简单。

2.2.            CppUnit Extension 解析

虽然CppUnit的核心很简单,但是需要编写测试用例的代码却比较复杂,所以CppUnit提供了扩展程序以及宏定义来帮助用户更好的使用CppUnit。Extension的类图如图2所示:

图2 CppUnit Extension 类图

Extenstion主要包括以下几大部分:

2.2.1.      工厂类

TestFactory        Test工厂的虚基类

TestSuiteFactory   TestSuite工厂类,用于产生具体的TestSuite

TestFactoryRegistry该类有两个功能,第一个功能是作为仓库,提供接口让其他模块注册新的TestFactory。第二功能是作为TestFactory,调用注册进去的TestFactory生成Test,并且添加到一个统一的TestSuite中,并返回给调用者。

2.2.2.      辅助类

TestSuiteBuilderContextBase该类是Extension中非常重要的一个类,该类用于将某个TestFixture中声明的TestCase添加到该TestFixture对应的TestSuite中。关于该类的作用可以等到解析HelperMacro时再分析。

TestNamer一个辅助类用于定义以及生成测试的名称。

2.2.3.      帮助宏

主要包括以下四个宏:

1. CPPUNIT_TEST_SUITE

2. CPPUNIT_TEST

3. CPPUNIT_TEST_SUITE_END

4. CPPUNIT_TEST_SUITE_REGISTRATION

在上面的例子中,我们已经知道了这几个宏定义如何使用,下面我们就剖析这几个宏定义如何起作用的。

CPPUNIT_TEST_SUITE实现如下:

#define CPPUNIT_TEST_SUITE(ATestFixtureType)                              \

  public:                                                                  \

    typedef ATestFixtureTypeTestFixtureType;                               \

                                                                           \

  private:                                                                 \

    static constCPPUNIT_NS::TestNamer&getTestNamer__()                    \

    {                                                                      \

      static CPPUNIT_TESTNAMER_DECL(testNamer,ATestFixtureType);         \

      return testNamer;                                                    \

    }                                                                      \

                                                                           \

  public:                                                                  \

    typedef CPPUNIT_NS::TestSuiteBuilderContext<TestFixtureType>           \

                TestSuiteBuilderContextType;                                \

                                                                           \

    static void                                                            \

    addTestsToSuite( CPPUNIT_NS::TestSuiteBuilderContextBase &baseContext ) \

    {                                                                      \

      TestSuiteBuilderContextTypecontext(baseContext)

 

从上面我们可以看出,CPPUNIT_TEST_SUITE定义了一个接口获取TestNamer, 同时定义了一个未结尾的函数addTestsToSuite,在这个函数中只声明了一个TestSuiteBuilderContext 类型的变量context。

 

CPPUNIT_TEST实现如下:

#define CPPUNIT_TEST(testMethod)                        \

    CPPUNIT_TEST_SUITE_ADD_TEST(                           \

        ( new CPPUNIT_NS::TestCaller<TestFixtureType>(    \

                 context.getTestNameFor(#testMethod),   \

                 &TestFixtureType::testMethod,           \

                 context.makeFixture()) ) )

#define CPPUNIT_TEST_SUITE_ADD_TEST(test ) \

      context.addTest(test )

如上所示,CPPUNIT_TEST只是调用TestCaller生成了一个TestCase,并将这个TestCase添加到CPPUNIT_TEST_SUITE声明的变量context中。

 

CPPUNIT_TEST_SUITE_END实现如下:

#define CPPUNIT_TEST_SUITE_END()                                               \

    }                                                                         \

                                                                              \

    static CPPUNIT_NS::TestSuite *suite()                                      \

    {                                                                         \

      const CPPUNIT_NS::TestNamer &namer=getTestNamer__();                   \

      std::auto_ptr<CPPUNIT_NS::TestSuite>suite(                              \

             new CPPUNIT_NS::TestSuite(namer.getFixtureName() ));             \

      CPPUNIT_NS::ConcretTestFixtureFactory<TestFixtureType>factory;          \

      CPPUNIT_NS::TestSuiteBuilderContextBasecontext( *suite.get(),          \

                                                      namer,                  \

                                                      factory );              \

      TestFixtureType::addTestsToSuite(context );                             \

      return suite.release();                                                 \

    }                                                                         \

  private: /* dummy typedefso that the macro can still end with ';'*/         \

typedef int CppUnitDummyTypedefForSemiColonEnding__

如上所示,CPPUNIT_TEST_SUITE_END首先是一个”}”用于结束addTestsToSuite函数的定义,然后定义了一个静态函数suite生成一个TestSuite对象,并且定义了TestSuiteBuilderContextBase变量,然后调用addTestsToSuite将用户使用CPPUNIT_TEST定义的Test都添加到TestSuite对象中。

 

CPPUNIT_TEST_SUITE_REGISTRATION实现:

#define CPPUNIT_TEST_SUITE_REGISTRATION(ATestFixtureType )      \

  static CPPUNIT_NS::AutoRegisterSuite<ATestFixtureType>       \

             CPPUNIT_MAKE_UNIQUE_NAME(autoRegisterRegistry__)

从上可以看出该宏只定义了一个AutoRegisterSuite 的静态变量,而通过分析AutoRegisterSuite我们知道该类的作用是生成一个当前TestFixture对应的TesSuiteFactory,同时添加到TestFactoryRegistry中。同时我们分析TesSuiteFactory知道该工厂就是通过调用静态的suite接口创建一个具体的Test。

这样,整个流程就如图3所示:

图3 Helper Marco流程

 

你可能感兴趣的:(CppUnit使用教程以及原理分析)