• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            Just enjoy programming

            CppUnit快速入門(轉(zhuǎn)載)

            簡介

            測試是軟件開發(fā)過程中極其重要的一環(huán),詳盡周密的測試能夠減少軟件BUG,提高軟件品質(zhì)。測試包括單元測試、系統(tǒng)測試等。其中單元測試是指針對軟件功能單元所作的測試,這里的功能單元可以是一個類的屬性或者方法,測試的目的是看這些基本單元是否工作正常。由于單元測試的內(nèi)容很基礎(chǔ),因此可以看作是測試工作的第一環(huán),該項(xiàng)工作一般由開發(fā)人員自行完成。如果條件允許,單元測試代碼的開發(fā)應(yīng)與程序代碼的開發(fā)同步進(jìn)行。

            雖然不同程序的單元測試代碼不盡相同,但測試代碼的框架卻非常相似,于是便出現(xiàn)了一些單元測試類庫,CppUnit便是其中之一。

            CppUnit是XUnit中的一員,XUnit是一個大家族,還包括JUnit和PythonUnit等。CppUnit簡單實(shí)用,學(xué)習(xí)和使用起來都很方便,網(wǎng)上已有一些文章對其作介紹,但本文更著重于講解其中的基本概念和使用方法,以幫助初次接觸CppUnit的人員快速入門。

            安裝

            目前,CppUnit的最新版本是1.10.2,你可以從下面地址獲取:

            http://sourceforge.net/projects/cppunit

            解壓后,你可以看到CppUnit包含如下目錄:

            config: 配置文件 contrib: contribution,其他人貢獻(xiàn)的外圍代碼 doc: 文檔,需要通過doxygen工具生成,也可以直接從sourceforge站點(diǎn)上下載打包好的文檔 examples:示例代碼 include: 頭文件 lib: 存放編譯好的庫 src: 源文件,以及編譯庫的工程等

            然后打開src目錄下的CppUnitLibraries工程,執(zhí)行build/batch build,編譯成功的話,生成的庫文件將被拷貝到lib目錄下。

            你也可以根據(jù)需要選擇所需的項(xiàng)目進(jìn)行編譯,其中項(xiàng)目cppunit為靜態(tài)庫,cppunit_dll為動態(tài)庫,生成的庫文件為:

            cppunit.lib: 靜態(tài)庫release版 cppunitd.lib: 靜態(tài)庫debug版 cppunit_dll.lib: 動態(tài)庫release版 cppunitd_dll.lib:動態(tài)庫debug版

            要使用CppUnit,還得設(shè)置好頭文件和庫文件路徑,以VC6為例,選擇Tools/Options/Directories,在Include files和Library files中分別添加%CppUnitPath%/include和%CppUnitPath%/lib,其中%CppUnitPath%表示CppUnit所在路徑。

            做好準(zhǔn)備工作后,我們就可以編寫自己的單元測試代碼了。需說明的是,CppUnit所用的動態(tài)運(yùn)行期庫均為多線程動態(tài)庫,因此你的單元測試程序也得使用相應(yīng)設(shè)置,否則會發(fā)生沖突。

            概念

            在使用之前,我們有必要認(rèn)識一下CppUnit中的主要類,當(dāng)然你也可以先看后面的例子,遇到問題再回過頭來看這一節(jié)。

            CppUnit核心內(nèi)容主要包括六個方面,

            1. 測試對象(Test,TestFixture,...):用于開發(fā)測試用例,以及對測試用例進(jìn)行組織管理。

            2. 測試結(jié)果(TestResult):處理測試用例執(zhí)行結(jié)果。TestResult與下面的TestListener采用的是觀察者模式(Observer Pattern)。

            3. 測試結(jié)果監(jiān)聽者(TestListener):TestListener作為TestResult的觀察者,擔(dān)任實(shí)際的結(jié)果處理角色。

            4. 結(jié)果輸出(Outputter):將結(jié)果進(jìn)行輸出,可以制定不同的輸出格式。

            5. 對象工廠(TestFactory):用于創(chuàng)建測試對象,對測試用例進(jìn)行自動化管理。

            6. 測試執(zhí)行體(TestRunner):用于運(yùn)行一個測試。

            以上各模塊的主要類繼承結(jié)構(gòu)如下:

            Test TestFixture TestResult TestListener _______|_________ | | | | | TestSuccessListener TestComposite TestLeaf | | | |____________| TestResultCollector TestSuit | TestCase | TestCaller<Fixture> Outputter TestFactory TestRunner ____________________|_________________ | | | | TestFactoryRegistry CompilerOutputter TextOutputter XmlOutputter | TestSuiteFactory<TestCaseType>

            接下來再對其中一些關(guān)鍵類作以介紹。

            Test:所有測試對象的基類。

            CppUnit采用樹形結(jié)構(gòu)來組織管理測試對象(類似于目錄樹),因此這里采用了組合設(shè)計(jì)模式(Composite Pattern),Test的兩個直接子類TestLeaf和TestComposite分別表示“測試樹”中的葉節(jié)點(diǎn)和非葉節(jié)點(diǎn),其中TestComposite主要起組織管理的作用,就像目錄樹中的文件夾,而TestLeaf才是最終具有執(zhí)行能力的測試對象,就像目錄樹中的文件。

            Test最重要的一個公共接口為:

            virtual void run(TestResult *result) = 0;

            其作用為執(zhí)行測試對象,將結(jié)果提交給result。

            在實(shí)際應(yīng)用中,我們一般不會直接使用Test、TestComposite以及TestLeaf,除非我們要重新定制某些機(jī)制。

            TestFixture:用于維護(hù)一組測試用例的上下文環(huán)境。

            在實(shí)際應(yīng)用中,我們經(jīng)常會開發(fā)一組測試用例來對某個類的接口加以測試,而這些測試用例很可能具有相同的初始化和清理代碼。為此,CppUnit引入TestFixture來實(shí)現(xiàn)這一機(jī)制。

            TestFixture具有以下兩個接口,分別用于處理測試環(huán)境的初始化與清理工作:

            virtual void setUp(); 
            virtual void tearDown(); 

            TestCase:測試用例,從名字上就可以看出來,它便是單元測試的執(zhí)行對象。

            TestCase從Test和TestFixture多繼承而來,通過把Test::run制定成模板函數(shù)(Template Method)而將兩個父類的操作融合在一起,run函數(shù)的偽定義如下:

            // 偽代碼 
            void TestCase::run(TestResult* result)
            {
                result->startTest(this); // 通知result測試開始
                if( result->protect(this, &TestCase::setUp) ) // 調(diào)用setUp,初始化環(huán)境
                    result->protect(this, &TestCase::runTest); // 執(zhí)行runTest,即真正的測試代碼
                result->protect(this, &TestCase::tearDown); // 調(diào)用tearDown,清理環(huán)境
                result->endTest(this); // 通知result測試結(jié)束
            }

            這里要提到的是函數(shù)runTest,它是TestCase定義的一個接口,原型如下:

            virtual void runTest();

            用戶需從TestCase派生出子類并實(shí)現(xiàn)runTest以開發(fā)自己所需的測試用例。

            另外還要提到的就是TestResult的protect方法,其作用是對執(zhí)行函數(shù)(實(shí)際上是函數(shù)對象)的錯誤信息(包括斷言和異常等)進(jìn)行捕獲,從而實(shí)現(xiàn)對測試結(jié)果的統(tǒng)計(jì)。

            TestSuit:測試包,按照樹形結(jié)構(gòu)管理測試用例

            TestSuit是TestComposite的一個實(shí)現(xiàn),它采用vector來管理子測試對象(Test),從而形成遞歸的樹形結(jié)構(gòu)。

            TestCaller:TestCase適配器(Adapter),它將成員函數(shù)轉(zhuǎn)換成測試用例

            雖然我們可以從TestCase派生自己的測試類,但從TestCase類的定義可以看出,它只能支持一個測試用例,這對于測試代碼的組織和維護(hù)很不方便,尤其是那些有共同上下文環(huán)境的一組測試。為此,CppUnit提供了TestCaller以解決這個問題。

            TestCaller是一個模板類,它以實(shí)現(xiàn)了TestFixture接口的類為模板參數(shù),將目標(biāo)類中某個符合runTest原型的測試方法適配成TestCase的子類。

            在實(shí)際應(yīng)用中,我們大多采用TestFixture和TestCaller相組合的方式,具體例子參見后文。

            TestResult和TestListener:處理測試信息和結(jié)果

            前面已經(jīng)提到,TestResult和TestListener采用了觀察者模式,TestResult維護(hù)一個注冊表,用于管理向其登記過的TestListener,當(dāng)TestResult收到測試對象(Test)的測試信息時,再一一分發(fā)給它所管轄的TestListener。這一設(shè)計(jì)有助于實(shí)現(xiàn)對同一測試的多種處理方式。

            TestFactory:測試工廠

            這是一個輔助類,通過借助一系列宏定義讓測試用例的組織管理變得自動化。參見后面的例子。

            TestRunner:用于執(zhí)行測試用例

            TestRunner將待執(zhí)行的測試對象管理起來,然后供用戶調(diào)用。其接口為:

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

            這也是一個輔助類,需注意的是,通過addTest添加到TestRunner中的測試對象必須是通過new動態(tài)創(chuàng)建的,用戶不能刪除這個對象,因?yàn)門estRunner將自行管理測試對象的生命期。

            使用

            先讓我們看看一個簡單的例子:

            #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); // 準(zhǔn)備好結(jié)果收集器 

                SimpleTest t;
                t.run(&r); // 運(yùn)行測試用例

                CppUnit::TextOutputter o(&rc, std::cout);
                o.write(); // 將結(jié)果輸出

                return 0;
            }
            編譯后運(yùn)行,輸出結(jié)果為:
            !!!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是其中之一,當(dāng)斷言失敗時,CppUnit便會將錯誤信息報(bào)告給TestResult。這些宏定義的說明如下:

            CPPUNIT_ASSERT(condition):判斷condition的值是否為真,如果為假則生成錯誤信息。

            CPPUNIT_ASSERT_MESSAGE(message, condition):與CPPUNIT_ASSERT類似,但結(jié)果為假時報(bào)告messsage信息。

            CPPUNIT_FAIL(message):直接報(bào)告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,用于浮點(diǎn)數(shù)比較。

            CPPUNIT_ASSERT_THROW(expression, ExceptionType):判斷執(zhí)行表達(dá)式expression后是否拋出ExceptionType異常。

            CPPUNIT_ASSERT_NO_THROW(expression):斷言執(zhí)行表達(dá)式expression后無異常拋出。

            接下來再看看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); // 準(zhǔn)備好結(jié)果收集器 

                CppUnit::TestRunner runner; // 定義執(zhí)行實(shí)體
                runner.addTest(new CppUnit::TestCaller<StringTest>("testSwap", &StringTest::testSwap)); // 構(gòu)建測試用例1
                runner.addTest(new CppUnit::TestCaller<StringTest>("testFind", &StringTest::testFind)); // 構(gòu)建測試用例2
                runner.run(r); // 運(yùn)行測試

                CppUnit::TextOutputter o(&rc, std::cout);
                o.write(); // 將結(jié)果輸出

                return rc.wasSuccessful() ? 0 : -1;
            }
            編譯后運(yùn)行結(jié)果為:
            OK (2 tests)

            上面的代碼從功能上講沒有什么問題,但編寫起來太繁瑣了,為此,我們可以借助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();  // 結(jié)束測試包定義
                
            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); // 準(zhǔn)備好結(jié)果收集器 

                CppUnit::TestRunner runner; // 定義執(zhí)行實(shí)體
                runner.addTest(CppUnit::TestFactoryRegistry::getRegistry().makeTest());
                runner.run(r); // 運(yùn)行測試

                CppUnit::TextOutputter o(&rc, std::cout);
                o.write(); // 將結(jié)果輸出

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

            CppUnit的簡單介紹就到此,相信你已經(jīng)了解了其中的基本概念,也能夠開發(fā)單元測試代碼了。

            轉(zhuǎn)自:http://blog.csdn.net/freefalcon/article/details/753819

            posted on 2012-03-01 20:25 周強(qiáng) 閱讀(244) 評論(0)  編輯 收藏 引用 所屬分類: 軟件工程


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久久精品人妻一区二区三区蜜桃| 久久精品国产网红主播| AV狠狠色丁香婷婷综合久久| 国产精品一区二区久久不卡 | 国产美女久久精品香蕉69| 色综合久久88色综合天天| 国产精品99久久久久久www| 久久青青草原亚洲av无码| 久久夜色精品国产噜噜麻豆 | 三上悠亚久久精品| 青青青伊人色综合久久| 亚洲午夜精品久久久久久浪潮| 人妻无码αv中文字幕久久| A级毛片无码久久精品免费| 影音先锋女人AV鲁色资源网久久| 99久久国产综合精品麻豆| 波多野结衣久久一区二区| 久久99国内精品自在现线| 热久久最新网站获取| 国产精品99久久精品爆乳| 国产麻豆精品久久一二三| 亚洲乱码精品久久久久..| 久久久久国产亚洲AV麻豆| 国产欧美一区二区久久| 久久久久亚洲AV片无码下载蜜桃| 久久毛片免费看一区二区三区| AAA级久久久精品无码片| 日韩欧美亚洲综合久久| 香蕉久久永久视频| 久久久WWW免费人成精品| 国产视频久久| 精品人妻伦一二三区久久| 国产高潮国产高潮久久久91| 久久精品国产亚洲欧美| 精品久久久久香蕉网| 色综合久久中文字幕无码| 97香蕉久久夜色精品国产| 一级做a爰片久久毛片免费陪| 久久久久这里只有精品| 久久这里只有精品视频99| 色婷婷狠狠久久综合五月|