使用CppUnit编写测试程序

本文详细介绍了如何使用CppUnit编写测试程序,包括创建测试用例、运行测试、使用TestResult控制器、TestSuite和TestCaller,以及在Windows Console和MFC对话框UI中的应用。文章还展示了使用CPPUNIT_TEST_SUITE宏注册测试套件的例子。
摘要由CSDN通过智能技术生成

******************************************************************************
* 使用CppUnit编写测试程序的大致流程
* 20050823 by daineng@nj.cpsecure
******************************************************************************

  ****************************************************************************
  * 需要先知道的:
  * 除了头文件的路径,库文件(或许还用的上动态库),CodeGeneration还需要设置
  * 成 Multi-threaded Debug Dll
  ****************************************************************************


  * 最简单的一个例子
    --------------------------------------------------------------------------

    class MyTestCase : public CPPUNIT_NS::TestCase {
     public:
        virtual void runTest() {
            CPPUNIT_ASSERT_EUQAL(1, 2);
        }
    };

    然后

    MyTestCase case;
    case.runTest();

    CPPUNIT_ASSERT_EQUAL是一个宏,在它的两个参数不相等的时候,会抛出异常。


  * Windows Console
    --------------------------------------------------------------------------

    MyTestCase case;
    CPPUNIT_NS::TestResult result;
    CPPUNIT_NS:;TestResultCollector collector;
    result.addListener(&collector);
    case.run(&result);
    CPPUNIT_NS::TextOutputter out(&collector, std::cout);
    out.write();

    # 似乎在这里不需要动态库文件

    TestResult实际上是一个测试的控制器,在调用TestCase.run()时,需要提供一个
    TestResult。run作为测试的进行方,会把测试中产生的信息发送给TestResult,
    而TestResult作为一个分发器,会把所受到的消息再转发给Listener。也就是说,
    如果简单定义一个TestResult并且把它的指针传给TestCase::run(),这个程序也
    能够编译通过并正确运行,但是不会有任何输出。TestResultCollector可以把测
    试输出的信息都收集起来,并且最后通过TextOutputter输出。

    同样 CPPUNIT_ASSERT_EQUAL 在测试不相等时会抛出异常,但这里不会异常退出。
    TestCase.run()使用特殊的机制把函数包起来,任何异常都会被捕获。


  * More Test
    --------------------------------------------------------------------------

    以上只能测试一个实例(一个过程,在TestCase.runTest()中指定),如果要同时
    测试多个过程的话,MyTestCase则需要从CPPUNIT_NS::TestFixture继承。为区别,
    使用 class MyTestFixture : public CPPUNIT::TestFixture

    TestCase    : This class is used to implement a simple test case.
    TestFixture : A TestFixture is used to provide a common environment for
                  a set of test case.

    # 注意到,TestCase的一个父类就是 TestFixture。TestCase提供了一个可使用的
      测试接口:run() 和 runTest()
      class CPPUNIT_API TestCase : public TestLeaf,
                                   public TestFixture

    TestFixture提供了setUp()和tearDown()函数提供测试前的初始化和测试后的清理
    工作。我们可以在MyTestFixture实现几个自己的测试函数(测试过程):
      testFunction1();
      testFunction2();

    为run这些测试过程,需要以下的过程:

    CPPUNIT_NS::Test *test1 = new CPPUNIT_NS::TestCaller<MyTestFixture>(
        "testFunction1", &MyTestFixture::testFunction1);
    CPPUNIT_NS::Test *test2 = new CPPUNIT_NS::TestCaller<MyTestFixture>(
        "testFunction2", &MyTestFixture::testFunction2);
    test1->run();
    test2->run();
   
    # 以上是CppUnit中TextFixture.h中的示例,实际上更直观的是不要使用
      CPPUNIT_NS::Test对象,直接使用TestCaller对象,如下:

    CPPUNIT_NS::TestCaller<MyTestFixture> testcaller1 (
        "testFunction1", &MyTestFixture::testFunction1);
    CPPUNIT_NS::TestCaller<MyTestFixture> testcaller2 (
        "testFunction2", &MyTestFixture::testFunction2);
    testcaller1.run(result);
    testcaller2.run(result);

    CPPUNIT_NS::TestCaller
      A test caller provides access to a test case method on a test
      fixture class. Test callers are useful when you want to run an
      individual test or add it to a suite.
      Test Callers invoke only one Test (i.e. test method) on one
      Fixture of a TestFixture.

    #
    ######
    #

    到目前为止,涉及到的情况仍是单一的测试过程,要测试一批过程,首先要将所有
    的过程逐个添加到一个集合中去,下面先从父类到子类的顺序介绍这个集合。

    class CPPUNIT_API Test

      All test objects should be a subclass of Test.  Some test objects,
      TestCase for example, represent one individual test.  Other test
      objects, such as TestSuite, are comprised of several tests.

      CPPUNIT_NS::Test提供了一个run()函数,剩下的部分实现的功能如同一个链表
      的节点需要的那样。

    class CPPUNIT_API TestComposite : public Test

      Base class for all test composites. Subclass this class if you need
      to implement a custom TestSuite.

    class CPPUNIT_API TestSuite : public TestComposite

      A Composite of Tests.

      TestSuite既继承自TestComposite,也包含一个由Test构成的向量。可以看得出
      它能够递归地构成一棵树。

    往TestSuite中添加测试过程:

    CPPUNIT_NS::TestSuite *suite = new CPPUNIT_NS::TestSuite;
    suite->addTest( new CPPUNIT_NS::TestCaller<MyTestFixture>(
        "testFunction1", testFunction1 ) );
    suite->addTest( new CPPUNIT_NS::TestCaller<MyTestFixture>(
        "testFunction2", testFunction2 ) );

    上面的过程通常定义在MyTestCase或MyTestFixture中,并且返回这个suite,
    返回的类型为CPPUNIT_NS::Test*

    和第一次提到Test这个类一样,我们碰到了一个问题,Test和TestCaller似乎是两
    个不相关的类,怎么能够将 TestCaller* 赋值给 Test*?
    # void TestSuite::addTest( Test *test )

    答案在于TestCaller的父类TestCase的一个父类是TestLeaf,而TestLeaf又继承于
    Test。可以这样看,TestFixture, TestCase, TestCaller构成测试实体,而Test,
    TestComposite, TestSuite构成测试节点/集合。

    CppUnit提供了一组宏来方便做addTest()的工作:

      CPPUNIT_TEST_SUITE(MyTestFixture);
        CPPUNIT_TEST(testFunction1);
        CPPUNIT_TEST(testFunction2);
      CPPUNIT_TEST_END();

    这组宏还实现了一个suite函数返回 TestSuite*

    到目前为止我们得到了一个包含许多测试过程的集合suite,下面介绍怎么run这个
    集合中的测试过程。

    CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest->run(&result);

    getRegistry(const std::string &name)是TestFactoryRegistry的静态函数,通
    过一个名字返回一个TestFactoryRegistry的对象引用(有默认的名字),
    makeTest()是TestFactoryRegistry的成员函数,返回Test的指针,用这个指针就
    可以调用run()函数来进行测试了。

    要使getRegistry得到MyTestFactory,一般要在MyTestFixture的源文件中使用:
      CPPUNIT_TEST_SUITE_REGISTRATION( MyTestFixture );
    这个宏实际上产生了一个静态全局的AutoRegisterSuite对象,细节不再讨论。

    另外,使用CPPUNIT_TEST_SUITE_NAMED_REGISTRATION宏可以将MyTestFixture与
    一个有名字的TestFactoryRegistry联系起来,要得到这个TestFactoryRegistry
    对象引用的方式只需要在getRegistry()函数的参数中指定这个名字就可以了。
 


  * MFC DIALOG UI
    --------------------------------------------------------------------------

    /*********************************************
     * Header Needed:
     * cppunit/ui/mfc/TestRunner.h
     *********************************************/
    CPPUNIT_NS::MfcUi::TestRunner runner;
    runner.addTest(MY_TEST_NS::suite());    // 这个函数将runner和TEST-SUITE
                                            // 联系起来
    runner.run();   // 调用CppUnit的测试对话框

    MY_TEST_NS::suite()代表测试的入口点,其实现如下:

    /*********************************************
     * Header Needed:
     * cppunit/extensions/TestFactoryRegistry.h
     * cppunit/TestSuite.h
     *********************************************/
    namespace MY_TEST_NS {
        CPPUNIT_NS:Test* suite() {
            CPPUNIT_NS::TestFactoryRegistry &registry =
                CPPUNIT_NS::TestFactoryRegistry::getRegistry(SuiteName());
            return registry.makeTest();
        }
    }   // 这里似乎没有用到TestSuite类

    SuiteName是MY_TEST_NS域中的一个使用者实现的函数,简单返回一个const
    std::string,这个字符串是测试入口点的名字。

    有了Test-Suite之后,接下来要准备Test-Case:

    /*********************************************
     * Header Needed:
     * cppunit/extensions/HelperMacro.h
     * 这个头文件包含了<cppunit/TestCaller.h>和
     * <cppunit/TestSuite.h>,其中前者包含了
     * <cppunit/TestCase.h>
     *********************************************/
    class CMyTestCase : public CPPUNIT_NS::TestCase {
        CPPUNIT_TEST_SUITE(CMyTestCase);
        CPPUNIT_TEST(TEST_FUNC);    // TEST_FUNC是CMyTestCase的成员函数
                                    // 一个CASE下可以有多个函数,只需要
                                    // 重复使用CPPUNIT_TEST宏
        CPPUNIT_TEST_SUITE_END();

     public:
        void TEST_FUNC();
    };

    最后,将一个TEST-CASE加入一个TEST-SUITE:

    CPPUNIT_TEST_SUITE_NAMED_REGISTRATION(CMyTestCase,
        MY_TEST_NS::SuiteName());

    同样可以使用多次以将更多的TEST-CASE加入TEST_SUITE中。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值