青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

隨筆 - 298  文章 - 377  trackbacks - 0
<2008年6月>
25262728293031
1234567
891011121314
15161718192021
22232425262728
293012345

常用鏈接

留言簿(34)

隨筆分類

隨筆檔案

文章檔案

相冊

收藏夾

搜索

  •  

最新評論

閱讀排行榜

評論排行榜

[文章來源]

           引用

[文章內容]

簡介

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

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

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

安裝

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

http://sourceforge.net/projects/cppunit

解壓后,你可以看到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             _______|_________            |                                    |                  |               |            |                           TestSuccessListener    TestComposite   TestLeaf         |                                    |                  |               |____________|                           TestResultCollector              TestSuit                  |                           TestCase                                                   |                      TestCaller<Fixture>                                              Outputter                                    TestFactory                    TestRunner        ____________________|_________________                            |        |                   |                |                   TestFactoryRegistry    CompilerOutputter  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將自行管理測試對象的生命期。

使用

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

#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后無異常拋出。

接下來再看看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)

上面的代碼從功能上講沒有什么問題,但編寫起來太繁瑣了,為此,我們可以借助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的簡單介紹就到此,相信你已經了解了其中的基本概念,也能夠開發單元測試代碼了。

posted on 2008-06-18 12:21 聶文龍 閱讀(2565) 評論(6)  編輯 收藏 引用

FeedBack:
# re: 【CppUnit】快速入門 2008-06-18 12:33 聶文龍
如果以上編譯安裝沒有出錯。CPPUNIT會在系統的/usr/local/bin下安裝cppunit-config文件。可以運行如下命令進行驗證。

查看版本信息

$cppunit-config --version

查看編譯標志

$cppunit-config --cflags

查看鏈接標志

$cppunit-config --libs
  回復  更多評論
  
# re: 【CppUnit】快速入門 2008-06-18 15:53 聶文龍

便利的開發工具 CppUnit 快速使用指南

CppUnit 是個基于 LGPL 的開源項目,最初版本移植自 JUnit,是一個非常優秀的開源測試框架。CppUnit 和 JUnit 一樣主要思想來源于極限編程(XProgramming)。主要功能就是對單元測試進行管理,并可進行自動化測試。這樣描述可能沒有讓您體會到測試框架的強大威力,那您在開發過程中遇到下列問題嗎?如果答案是肯定的,就應該學習使用這種技術:

測試代碼沒有很好地維護而廢棄,再次需要測試時還需要重寫;
投入太多的精力,找 bug,而新的代碼仍然會出現類似 bug;
寫完代碼,心里沒底,是否有大量 bug 等待自己;
新修改的代碼不知道是否影響其他部分代碼;
由于牽扯太多,導致不敢進行修改代碼;
...
這些問題下文都會涉及。這個功能強大的測試框架在國內的 C++ 語言開發人員中使用的不是很多。本文從開發人員的角度,介紹這個框架,希望能夠使開發人員用最少的代價盡快掌握這種技術。下面從基本原理,CppUnit 原理,手動使用步驟,通常使用步驟,其他實際問題等方面進行討論。以下討論基于 CppUnit1.8.0。

1. 基本原理

對于上面的問題僅僅說明 CppUnit 的使用是沒有效果的,下面先從測試的目的,測試原則等方面簡要說明,然后介紹 CppUnit 的具體使用。

首先要明確我們寫測試代碼的目的,就是驗證代碼的正確性或者調試 bug。這樣寫測試代碼時就有了針對性,對那些容易出錯的,易變的編寫測試代碼;而不用對每個細節,每個功能編寫測試代碼,當然除非有過量精力或者可靠性要求。

編碼和測試的關系是密不可分的,推薦的開發過程并不要等編寫完所有或者很多的代碼后再進行測試,而是在完成一部分代碼,比如一個函數,之后立刻編寫測試代碼進行驗證。然后再寫一些代碼,再寫測試。每次測試對所有以前的測試都進行一遍。這樣做的優點就是,寫完代碼,也基本測試完一遍,心里對代碼有信心。而且在寫新代碼時不斷地測試老代碼,對其他部分代碼的影響能夠迅速發現、定位。不斷編碼測試的過程也就是對測試代碼維護的過程,以便測試代碼一直是有效的。有了各個部分測試代碼的保證,有了自動測試的機制,更改以前的代碼沒有什么顧慮了。在極限編程(一種軟件開發思想)中,甚至強調先寫測試代碼,然后編寫符合測試代碼的代碼,進而完成整個軟件。

根據上面說的目的、思想,下面總結一下平時開發過程中單元測試的原則:

先寫測試代碼,然后編寫符合測試的代碼。至少做到完成部分代碼后,完成對應的測試代碼;
測試代碼不需要覆蓋所有的細節,但應該對所有主要的功能和可能出錯的地方有相應的測試用例;
發現 bug,首先編寫對應的測試用例,然后進行調試;
不斷總結出現 bug 的原因,對其他代碼編寫相應測試用例;
每次編寫完成代碼,運行所有以前的測試用例,驗證對以前代碼影響,把這種影響盡早消除;
不斷維護測試代碼,保證代碼變動后通過所有測試;
有上面的理論做指導,測試行為就可以有規可循。那么 CppUnit 如何實現這種測試框架,幫助我們管理測試代碼,完成自動測試的?下面就看看 CppUnit 的原理。

2. CppUnit 的原理

在 CppUnit 中,一個或一組測試用例的測試對象被稱為 Fixture(設施,下文為方便理解盡量使用英文名稱)。Fixture 就是被測試的目標,可能是一個對象或者一組相關的對象,甚至一個函數。

有了被測試的 fixture,就可以對這個 fixture 的某個功能、某個可能出錯的流程編寫測試代碼,這樣對某個方面完整的測試被稱為TestCase(測試用例)。通常寫一個 TestCase 的步驟包括:

對 fixture 進行初始化,及其他初始化操作,比如:生成一組被測試的對象,初始化值;
按照要測試的某個功能或者某個流程對 fixture 進行操作;
驗證結果是否正確;
對 fixture 的及其他的資源釋放等清理工作。
對 fixture 的多個測試用例,通常(1)(4)部分代碼都是相似的,CppUnit 在很多地方引入了 setUp 和 tearDown 虛函數。可以在 setUp 函數里完成(1)初始化代碼,而在 tearDown 函數中完成(4)代碼。具體測試用例函數中只需要完成(2)(3)部分代碼即可,運行時 CppUnit 會自動為每個測試用例函數運行 setUp,之后運行 tearDown,這樣測試用例之間就沒有交叉影響。

對 fixture 的所有測試用例可以被封裝在一個 CppUnit::TestFixture 的子類(命名慣例是[ClassName]Test)中。然后定義這個fixture 的 setUp 和 tearDown 函數,為每個測試用例定義一個測試函數(命名慣例是 testXXX)。下面是個簡單的例子:


class MathTest : public CppUnit::TestFixture {
protected:
int m_value1, m_value2;

public:
MathTest() {}

// 初始化函數
void setUp () {
m_value1 = 2;
m_value2 = 3;
}
// 測試加法的測試函數
void testAdd () {
// 步驟(2),對 fixture 進行操作
int result = m_value1 + m_value2;
// 步驟(3),驗證結果是否爭取
CPPUNIT_ASSERT( result == 5 );
}
// 沒有什么清理工作沒有定義 tearDown.
}



在測試函數中對執行結果的驗證成功或者失敗直接反應這個測試用例的成功和失敗。CppUnit 提供了多種驗證成功失敗的方式:


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時失敗



要把對 fixture 的一個測試函數轉變成一個測試用例,需要生成一個 CppUnit::TestCaller 對象。而最終運行整個應用程序的測試代碼的時候,可能需要同時運行對一個 fixture 的多個測試函數,甚至多個 fixture 的測試用例。CppUnit 中把這種同時運行的測試案例的集合稱為 TestSuite。而 TestRunner 則運行測試用例或者 TestSuite,具體管理所有測試用例的生命周期。目前提供了 3 類TestRunner,包括:


CppUnit::TextUi::TestRunner // 文本方式的TestRunner
CppUnit::QtUi::TestRunner // QT方式的TestRunner
CppUnit::MfcUi::TestRunner // MFC方式的TestRunner



下面是個文本方式 TestRunner 的例子:


CppUnit::TextUi::TestRunner runner;
CppUnit::TestSuite *suite= new CppUnit::TestSuite();

// 添加一個測試用例
suite->addTest(new CppUnit::TestCaller<MathTest> (
"testAdd", testAdd));

// 指定運行TestSuite
runner.addTest( suite );
// 開始運行, 自動顯示測試進度和測試結果
runner.run( "", true ); // Run all tests and wait



對測試結果的管理、顯示等功能涉及到另一類對象,主要用于內部對測試結果、進度的管理,以及進度和結果的顯示。這里不做介紹。

下面我們整理一下思路,結合一個簡單的例子,把上面說的思路串在一起。

3. 手動使用步驟

首先要明確測試的對象 fixture,然后根據其功能、流程,以及以前的經驗,確定測試用例。這個步驟非常重要,直接關系到測試的最終效果。當然增加測試用例的過程是個階段性的工作,開始完成代碼后,先完成對功能的測試用例,保證其完成功能;然后對可能出錯的部分,結合以前的經驗(比如邊界值測試、路徑覆蓋測試等)編寫測試用例;最后在發現相關 bug 時,根據 bug 完成測試用例。

比如對整數加法進行測試,首先定義一個新的 TestFixture 子類,MathTest,編寫測試用例的測試代碼。后期需要添加新的測試用例時只需要添加新的測試函數,根據需要修改 setUp 和 tearDown 即可。如果需要對新的 fixture 進行測試,定義新的 TestFixture 子類即可。注:下面代碼僅用來表示原理,不能編譯。


/// MathTest.h
// A TestFixture subclass.
// Announce: use as your owner risk.
// Author : liqun (liqun@nsfocus.com)
// Data : 2003-7-5

#include "cppunit/TestFixture.h"


class MathTest : public CppUnit::TestFixture {
protected:
int m_value1, m_value2;

public:
MathTest() {}

// 初始化函數
void setUp ();
// 清理函數
void tearDown();

// 測試加法的測試函數
void testAdd ();
// 可以添加新的測試函數
};

/// MathTest.cpp
// A TestFixture subclass.
// Announce: use as your owner risk.
// Author : liqun (liqun@nsfocus.com)
// Data : 2003-7-5

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

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

void MathTest::tearDown()
{

}

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



然后編寫 main 函數,把需要測試的測試用例組織到 TestSuite 中,然后通過 TestRuner 運行。這部分代碼后期添加新的測試用例時需要改動的不多。只需要把新的測試用例添加到 TestSuite 中即可。


/// main.cpp
// Main file for cppunit test.
// Announce: use as your owner risk.
// Author : liqun (liqun@nsfocus.com)
// Data : 2003-7-5
// Note : Cannot compile, only for study.
#include "MathTest.h"
#include "cppunit/ui/text/TestRunner.h"
#include "cppunit/TestCaller.h"
#include "cppunit/TestSuite.h"

int main()
{
CppUnit::TextUi::TestRunner runner;
CppUnit::TestSuite *suite= new CppUnit::TestSuite();

// 添加一個測試用例
suite->addTest(new CppUnit::TestCaller<MathTest> (
"testAdd", testAdd));

// 指定運行TestSuite
runner.addTest( suite );
// 開始運行, 自動顯示測試進度和測試結果
runner.run( "", true ); // Run all tests and wait
}



4. 常用使用方式

按照上面的方式,如果要添加新的測試用例,需要把每個測試用例添加到 TestSuite 中,而且添加新的 TestFixture 需要把所有頭文件添加到 main.cpp 中,比較麻煩。為此 CppUnit 提供了 CppUnit::TestSuiteBuilder,CppUnit::TestFactoryRegistry 和一堆宏,用來方便地把 TestFixture 和測試用例注冊到 TestSuite 中。下面就是通常的使用方式:


/// MathTest.h
// A TestFixture subclass.
// Announce: use as your owner risk.
// Author : liqun (liqun@nsfocus.com)
// Data : 2003-7-5

#include "cppunit/extensions/HelperMacros.h"


class MathTest : public CppUnit::TestFixture {
// 聲明一個TestSuite
CPPUNIT_TEST_SUITE( MathTest );
// 添加測試用例到TestSuite, 定義新的測試用例需要在這兒聲明一下
CPPUNIT_TEST( testAdd );
// TestSuite聲明完成
CPPUNIT_TEST_SUITE_END();
// 其余不變

protected:
int m_value1, m_value2;

public:
MathTest() {}

// 初始化函數
void setUp ();
// 清理函數
void tearDown();

// 測試加法的測試函數
void testAdd ();
// 可以添加新的測試函數
};

/// MathTest.cpp
// A TestFixture subclass.
// Announce: use as your owner risk.
// Author : liqun (liqun@nsfocus.com)
// Data : 2003-7-5

#include "MathTest.h"

// 把這個TestSuite注冊到名字為"alltest"的TestSuite中, 如果沒有定義會自動定義
// 也可以CPPUNIT_TEST_SUITE_REGISTRATION( MathTest );注冊到全局的一個未命名的TestSuite中.
CPPUNIT_TEST_SUITE_NAMED_REGISTRATION( MathTest, "alltest" );

// 下面不變

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

void MathTest::tearDown()
{

}

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


/// main.cpp
// Main file for cppunit test.
// Announce: use as your owner risk.
// Compile : g++ -lcppunit MathTest.cpp main.cpp
// Run : ./a.out
// Test : RedHat 8.0 CppUnit1.8.0
// Author : liqun ( a litthle modification. liqun@nsfocus.com)
// Data : 2003-7-5

// 不用再包含所有TestFixture子類的頭文件
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>

// 如果不更改TestSuite, 本文件后期不需要更改.

int main()
{
CppUnit::TextUi::TestRunner runner;

// 從注冊的TestSuite中獲取特定的TestSuite, 沒有參數獲取未命名的TestSuite.
CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry("alltest");
// 添加這個TestSuite到TestRunner中
runner.addTest( registry.makeTest() );
// 運行測試
runner.run();
}



這樣添加新的測試用例只需要在類定義的開始聲明一下即可。

5. 其他實際問題

通常包含測試用例代碼和被測試對象是在不同的項目中。應該在另一個項目(最好在不同的目錄)中編寫 TestFixture,然后把被測試的對象包含在測試項目中。

對某個類或者某個函數進行測試的時候,這個 TestFixture 可能引用了別的類或者別的函數,為了隔離其他部分代碼的影響,應該在源文件中臨時定義一些樁程序,模擬這些類或者函數。這些代碼可以通過宏定義在測試項目中有效,而在被測試的項目中無效。
  回復  更多評論
  
# re: 【CppUnit】快速入門 2008-06-18 16:20 末日之刃
很清楚,支持!  回復  更多評論
  
# re: 【CppUnit】快速入門 2008-06-18 16:27 聶文龍
你可以鏈接靜態庫也可以鏈接靜態庫。
(a) 鏈接靜態庫。編譯命令:
g++ -L/usr/local/lib/libcppunit.a mytest.cpp -lcppunit -ldl -o mytest

運行:
./mytest

結果:
Test::testHelloWorldHello, world!
: OK

(b) 鏈接動態庫。編譯命令:
g++ mytest.cpp -lcppunit -ldl -o mytest

運行:
./mytest
結果:
Test::testHelloWorldHello, world!
: OK

如果你沒有執行步驟(5),那么你也可以在每次運行之前設置下臨時的環境變量LD_LIBRARY_PATH命令如下:
export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH  回復  更多評論
  
# re: 【CppUnit】快速入門 2008-06-18 17:46 聶文龍
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/CompilerOutputter.h>
#include <cppunit/ui/text/TestRunner.h>
#include <cppunit/TestFixture.h>
#include <cppunit/extensions/HelperMacros.h>


#include "complex.h"
class complexNumberTest : public CppUnit::TestFixture {
private:
long_double_complex *m_10_1,*m_1_1,*m_11_2;
protected:
void setUp()
{
m_10_1 = new long_double_complex( 10, 1 );
m_1_1 = new long_double_complex( 1, 1 );
m_11_2 = new long_double_complex( 11, 2 );
}

void tearDown()
{
delete m_10_1;
delete m_1_1;
delete m_11_2;
}

void testEquality()
{
CPPUNIT_ASSERT( *m_10_1 == *m_10_1 );
CPPUNIT_ASSERT( !(*m_10_1 == *m_11_2) );
}
public:
complexNumberTest(){}
/* void testAddition()
{
CPPUNIT_ASSERT( *m_10_1 + *m_1_1 == *m_11_2 );
}*/
};

int main( void)
{

CppUnit::TextUi::TestRunner runner;
CppUnit::TestFactoryRegistry &registry = CppUnit::TestFactoryRegistry::getRegistry();
runner.addTest( registry.makeTest() );
bool wasSucessful = runner.run( "", false );
return wasSucessful;

}



[root@localhost cppunit-test]# g++ -L/usr/local b -I/usr/local/include test1.cpp -o test1
/tmp/cccaLfPs.o(.text+0x12): In function `main':
: undefined reference to `CppUnit::TextUi::TestRunner::TestRunner(CppUnit::Outputter *)'
/tmp/cccaLfPs.o(.text+0x1a): In function `main':
: undefined reference to `CppUnit::TestFactoryRegistry::getRegistry(void)'
/tmp/cccaLfPs.o(.text+0x44): In function `main':
: undefined reference to `CppUnit::TextUi::TestRunner::addTest(CppUnit::Test *)'
/tmp/cccaLfPs.o(.text+0x7f): In function `main':
: undefined reference to `CppUnit::TextUi::TestRunner::run(basic_string<char, string_char_traits<char>, __default_alloc_template<true, 0> >, bool, bool, bool)'
/tmp/cccaLfPs.o(.text+0x99): In function `main':
: undefined reference to `CppUnit::TextUi::TestRunner::~TestRunner(void)'
/tmp/cccaLfPs.o(.text+0xc0): In function `main':
: undefined reference to `CppUnit::TextUi::TestRunner::~TestRunner(void)'
collect2: ld returned 1 exit status
[root@localhost cppunit-test]#  回復  更多評論
  
# re: 【CppUnit】快速入門 2009-09-18 11:10 緊急求助
你好,我就是按上面介紹的步驟執行的操作并寫了程序,可編譯總是出現下面的錯誤,請你看看這是什么錯誤,多謝了。

$ g++ MoneyApp.cpp MoneyTest.cpp -lcppunit -o test
/cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cc36iu30.o:MoneyTest.cpp:(.text$_ZN9
MoneyTest5suiteEv[MoneyTest::suite()]+0x1c4): undefined reference to `CppUnit::T
estSuiteBuilderContextBase::~TestSuiteBuilderContextBase()'
/cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cc36iu30.o:MoneyTest.cpp:(.text$_ZN9
MoneyTest5suiteEv[MoneyTest::suite()]+0x267): undefined reference to `CppUnit::T
estSuiteBuilderContextBase::~TestSuiteBuilderContextBase()'
/cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cc36iu30.o:MoneyTest.cpp:(.text$_ZN7
CppUnit23TestSuiteBuilderContextI9MoneyTestED1Ev[CppUnit::TestSuiteBuilderContex
t<MoneyTest>::~TestSuiteBuilderContext()]+0x16): undefined reference to `CppUnit
::TestSuiteBuilderContextBase::~TestSuiteBuilderContextBase()'
/cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cc36iu30.o:MoneyTest.cpp:(.text$_ZN7
CppUnit27TestSuiteBuilderContextBaseC2ERKS0_[CppUnit::TestSuiteBuilderContextBas
e::TestSuiteBuilderContextBase(CppUnit::TestSuiteBuilderContextBase const&)]+0xb
): undefined reference to `vtable for CppUnit::TestSuiteBuilderContextBase'
/cygdrive/c/DOCUME~1/ADMINI~1/LOCALS~1/Temp/cc36iu30.o:MoneyTest.cpp:(.text$_ZN7
CppUnit23TestSuiteBuilderContextI9MoneyTestED0Ev[CppUnit::TestSuiteBuilderContex
t<MoneyTest>::~TestSuiteBuilderContext()]+0x16): undefined reference to `CppUnit
::TestSuiteBuilderContextBase::~TestSuiteBuilderContextBase()'
collect2: ld returned 1 exit status

Administrator@6013E40A38DE4D6 ~/money
$  回復  更多評論
  
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
              午夜国产精品视频| 久久久久久久久久久久久女国产乱 | 亚洲激情自拍| 久久亚洲欧美| 久久精品综合| 久久大逼视频| 亚洲午夜一区二区三区| 亚洲女同在线| 久久偷窥视频| 欧美大片国产精品| 亚洲精品视频在线看| 亚洲日韩欧美视频| 一本久久a久久精品亚洲| 一区二区三区日韩在线观看 | 欧美激情一区在线| 欧美激情一区二区三级高清视频 | 久久国产精品久久久久久| 久久久久久伊人| 欧美精品日韩一本| 国产麻豆精品在线观看| 国产自产2019最新不卡| 国产亚洲福利社区一区| 亚洲国产成人在线视频| 亚洲一线二线三线久久久| 久久久久久夜| 欧美成人官网二区| 中文国产一区| 免费亚洲一区二区| 国产欧美日韩一区二区三区| 亚洲精品视频一区二区三区| 欧美亚洲视频在线看网址| 欧美成人性生活| 亚洲一区二区三区在线视频| 欧美**人妖| 国产专区综合网| 亚洲伊人第一页| 亚洲国产精品成人精品| 午夜国产精品视频| 欧美三级小说| 久久九九国产精品| 亚洲影视中文字幕| 久久久噜噜噜久久| 欧美日韩精品二区第二页| 国产亚洲激情视频在线| 亚洲午夜激情网站| 欧美高清免费| 久久精品国产清高在天天线| 国产精品嫩草99av在线| 日韩视频不卡| 欧美国产日韩一区| 久久精品中文| 国产一区二区三区高清播放| 亚洲欧美成人精品| 久久不射网站| 亚洲一级网站| 国产一区二区三区在线观看免费视频 | 欧美成人精品一区| 国产女精品视频网站免费| 99精品免费| 欧美成人情趣视频| 久久免费视频在线| 在线成人性视频| 久久亚洲免费| 久久亚洲高清| 伊人婷婷欧美激情| 久久免费一区| 久久精品国产亚洲aⅴ| 亚洲网友自拍| 日韩一级大片| 麻豆精品91| 国内外成人免费激情在线视频网站| 日韩午夜在线视频| 亚洲国内欧美| 欧美va天堂| 亚洲精品美女91| 亚洲丁香婷深爱综合| 玖玖玖国产精品| 在线看成人片| 亚洲第一精品福利| 欧美精品免费视频| 亚洲毛片一区| 99成人在线| 国产伦一区二区三区色一情| 亚洲高清成人| 亚洲在线播放电影| 欧美精品久久久久久久久老牛影院| 最近看过的日韩成人| 亚洲精品美女| 国产精品美女久久久久久2018| 亚洲欧美美女| 久久国产一区| 日韩亚洲欧美一区| 亚洲一区二区三区四区视频| 国产精品久久久久久福利一牛影视| 亚洲线精品一区二区三区八戒| 亚洲无毛电影| 亚洲第一中文字幕| 国产日韩欧美在线视频观看| 亚洲综合欧美| 午夜精品久久久久久久久久久久久| 久久精品成人欧美大片古装| 国产情侣久久| 久久在线免费观看| 欧美激情1区2区3区| 欧美一区二区三区四区视频 | 久久男人资源视频| 欧美精品在线极品| 亚洲一级二级在线| 久久综合色综合88| 亚洲免费综合| 欧美粗暴jizz性欧美20| 欧美一区二区视频在线观看| 开心色5月久久精品| 亚洲线精品一区二区三区八戒| 欧美激情黄色片| 香蕉久久a毛片| 欧美在线亚洲| 在线中文字幕不卡| 久久人人爽爽爽人久久久| 在线视频亚洲欧美| 亚洲一区中文字幕在线观看| 亚洲精品日韩一| 欧美一区精品| 亚洲国产综合在线| 午夜亚洲影视| 午夜影视日本亚洲欧洲精品| 欧美国产日韩一区二区| 久热国产精品视频| 国产一区二区高清| 亚洲无限av看| 亚洲一区二区三区四区五区黄| 午夜精品亚洲| 国产一区二三区| 99国产精品自拍| **性色生活片久久毛片| 中日韩美女免费视频网站在线观看| 最新日韩av| 久久久久综合一区二区三区| 久久久之久亚州精品露出| 欧美性天天影院| 亚洲精品视频在线观看网站| 亚洲国产一区二区三区a毛片| 久久精品国产免费| 欧美一区二视频在线免费观看| 欧美日韩一视频区二区| a91a精品视频在线观看| 亚洲开发第一视频在线播放| 久久aⅴ国产欧美74aaa| 久久久亚洲精品一区二区三区 | 亚洲人成网在线播放| 亚洲一区影音先锋| 亚洲久久一区| 欧美日韩综合视频| 一区二区三区精品| 亚洲欧美日韩在线播放| 国产欧美一区二区白浆黑人| 午夜精品网站| 裸体歌舞表演一区二区| 亚洲激情一区| 欧美午夜美女看片| 亚洲男女自偷自拍图片另类| 欧美诱惑福利视频| 一区二区三区在线免费播放| 麻豆91精品| 亚洲最新合集| 久久精品免费| 亚洲精品四区| 国产欧美日韩不卡| 久久亚洲影院| 一本在线高清不卡dvd| 久久精品夜夜夜夜久久| 欧美日本在线一区| 欧美激情第9页| 亚洲网站在线看| 国产亚洲欧美日韩精品| 美女日韩欧美| 亚洲免费一级电影| 模特精品裸拍一区| 在线综合视频| 国产综合久久久久久| 毛片av中文字幕一区二区| 亚洲激情成人网| 欧美在线一二三四区| 亚洲欧洲综合另类在线| 国产精品一区免费在线观看| 麻豆av福利av久久av| 亚洲区在线播放| 久久久99久久精品女同性| 日韩午夜三级在线| 黄色成人av网| 国产精品毛片va一区二区三区| 久久资源在线| 亚洲主播在线| 欧美高清视频www夜色资源网| 亚洲资源在线观看| 亚洲国产精品一区二区三区| 国产精品日韩欧美一区| 欧美精品一区二区三区视频| 久久久精品免费视频| 亚洲欧美日韩直播|