您的位置:軟件測試 > 開源軟件測試 > 開源單元測試工具 > cppUnit
測試驅(qū)動(dòng)開發(fā)入門-CppUnit
作者:網(wǎng)絡(luò)轉(zhuǎn)載 發(fā)布時(shí)間:[ 2012/11/29 15:11:55 ] 推薦標(biāo)簽:

測試驅(qū)動(dòng)開發(fā)是一個(gè)現(xiàn)在軟件界流行的詞匯之一,可是很多人還是不得其門而入。這篇文章想通過對于CppUnit的介紹,給予讀者一個(gè)基本的映像。如果你熟知CppUnit的使用,請參閱我的另一篇文章:CppUnit代碼簡介 - 第一部分,核心類來獲得對于CppUnit進(jìn)一步的了解。

II. 測試驅(qū)動(dòng)開發(fā)
要理解測試驅(qū)動(dòng)開發(fā),必須先理解測試。測試是通過對源代碼的運(yùn)行或者別的方式的檢測來確定源代碼之中是否含有已知或者未知的錯(cuò)誤。所謂測試驅(qū)動(dòng)開發(fā),是在開發(fā)前根據(jù)對將要開發(fā)的程序的要求,先寫好所有測試代碼,并且在開發(fā)過程中不時(shí)地通過運(yùn)行測試代碼來獲得所開發(fā)的代碼與所要求的結(jié)果之間的差距。很多人可能會(huì)有疑問:既然我還沒有開始寫代碼,我怎么能夠?qū)憸y試代碼呢?這是因?yàn),雖然我們還沒有寫出任何實(shí)現(xiàn)代碼,但是我們可以根據(jù)我們對代碼的要求從使用者的角度寫出測試代碼。事實(shí)上,在開發(fā)前寫出測試代碼,可以檢測你的要求是不是完善和精確,因?yàn)槿绻銓懖怀鰷y試代碼,表示你的需求還不夠清晰。
這篇文章通過一個(gè)文件狀態(tài)操作類來展示測試驅(qū)動(dòng)開發(fā)相對于普通開發(fā)方法的優(yōu)勢。

III. 文件狀態(tài)操作類(FileStatus)需求
構(gòu)造函數(shù),接受一個(gè)const std::string&作為文件名參數(shù)。
DWORD getFileSize()函數(shù),獲取這個(gè)文件的長度。
bool fileExists()函數(shù),獲取這個(gè)文件是否存在。
void setFileModifyDate(FILETIME ft)函數(shù),設(shè)定這個(gè)文件的修改日期。
FILETIME getFileModifyDate()函數(shù),返回這個(gè)文件的修改日期。
std::string getFileName()函數(shù),返回這個(gè)文件的名字。

IV. CppUnit簡介
我們所進(jìn)行的測試,某種意義上說,是一個(gè)或者多個(gè)函數(shù)。通過對這些函數(shù)的運(yùn)行,我們可以檢測我們是否有錯(cuò)誤。假設(shè)我們要對構(gòu)造函數(shù)和getFileName函數(shù)進(jìn)行測試,這里面有一個(gè)很顯然的不變式,是對一個(gè)FileStatus::getFileName函數(shù)的調(diào)用,應(yīng)該與傳給這個(gè)FileStatus對象的構(gòu)造函數(shù)的參數(shù)相同。于是我們有這樣一個(gè)函數(shù):
bool testCtorAndGetFileName()
{
    const string fileName( "a.dat" );
    FileStatus status( fileName );
    return ( status.getFileName() == fileName );
}
我們只需要測試這個(gè)函數(shù)的返回值可以知道是否正確了。在CppUnit中,我們可以從TestCase派生出一個(gè)類,并且重載它的runTest函數(shù)。

class MyTestCase:public CPPUNIT_NS::TestCase
{
public:
    virtual void runTest()
    {
        const std::string fileName( "a.dat" );
        FileStatus status( fileName );
        CPPUNIT_ASSERT_EQUAL( status.getFileName(), fileName );
    }
};

CPPUNIT_ASSERT_EQUAL是一個(gè)宏,在它的兩個(gè)參數(shù)不相等的時(shí)候,會(huì)拋出異常。所以,理論上說,我們可以通過:

MyTestCase m;
m.runTest();

來進(jìn)行測試,如果有異常拋出,那么說明代碼寫錯(cuò)了?墒,這顯然不方便,也不是我們使用CppUnit的初衷。下面我們給出完整的代碼:

// UnitTest.cpp : Defines the entry point for the console application.
//

#include "CppUnit/TestCase.h"
#include "CppUnit/TestResult.h"
#include "CppUnit/TextOutputter.h"
#include "CppUnit/TestResultCollector.h"

#include
#include

class FileStatus
{
    std::string mFileName;
public:
    FileStatus( const std::string& fileName ):mFileName( fileName )
    {}
    std::string getFileName() const
    {
        return mFileName;
    }
};

class MyTestCase:public CPPUNIT_NS::TestCase
{
public:
    virtual void runTest()
    {
        const std::string fileName( "a.dat" );
        FileStatus status( fileName );
        CPPUNIT_ASSERT_EQUAL( status.getFileName(), fileName );
    }
};

int main()
{
    MyTestCase m;
    CPPUNIT_NS::TestResult r;
    CPPUNIT_NS::TestResultCollector result;
    r.addListener( &result );
    m.run( &r );
    CPPUNIT_NS::TextOutputter out( &result, std::cout );
    out.write();
    return 0;
}

這里我先說一下怎樣運(yùn)行這個(gè)程序。假設(shè)你的CppUnit版本是1.10.2,解壓后,你會(huì)在src文件夾中,發(fā)現(xiàn)一個(gè)CppUnitLibraries.dsw,打開它,并且編譯。你會(huì)在lib文件夾中,發(fā)現(xiàn)一些 lib和dll,我們的程序需要依賴當(dāng)中的某些。接著,創(chuàng)建一個(gè)Console應(yīng)用程序,假設(shè)我們僅使用Debug模式,在Project Settings中,把預(yù)編譯選項(xiàng)(Precompiled Header)選成No,把CppUnit的include路徑加入到Additional Include Directories中,并且把Code Generation改成Multi-threaded Debug Dll,接著把CppUnitD.lib加入到你的項(xiàng)目中去。后把我們的這個(gè)文件替換main.cpp。這個(gè)時(shí)候,可以編譯運(yùn)行了。
這個(gè)文件中,前面四行分別是CppUnit相應(yīng)的頭文件,在CppUnit中,通常某個(gè)類定義在用它的類名命名的頭文件中。接著是我們的string和 iostream頭文件。然后是我們類的一個(gè)簡單實(shí)現(xiàn),只實(shí)現(xiàn)了這個(gè)測試中有意義的功能。接下去是我們的TestCase的定義,CPPUNIT_NS是 CppUnit所在的名字空間。main中,TestResult其實(shí)是一個(gè)測試的控制器,你在調(diào)用TestCase的run時(shí),需要提供一個(gè) TestResult。run作為測試的進(jìn)行方,會(huì)把測試中產(chǎn)生的信息發(fā)送給TestResult,而TestResult作為一個(gè)分發(fā)器,會(huì)把所收到的信息再轉(zhuǎn)發(fā)給它的Listener。也是說,我簡單的定義一個(gè)TestResult并且把它的指針傳給TestCase::run,這個(gè)程序也能夠編譯通過并且正確運(yùn)行,但是它不會(huì)有任何輸出。TestResultCollector可以把測試輸出的信息都收集起來,并且后通過 TextOutputter輸出出來。在上述的例子中,你所獲得的輸出是:

OK (1 tests)

這說明我們一共進(jìn)行了1個(gè)測試,并且都通過了。如果我們?nèi)藶榈匕?quot;return mFileName;"改成"return mFileName + 'a';"以制造一個(gè)錯(cuò)誤,那么測試的結(jié)果會(huì)變成:

!!!FAILURES!!!
Test Results:
Run:  1   Failures: 1   Errors: 0


1) test:  (F) line: 31 c:unittestunittest.cpp
equality assertion failed
- Expected: a.data
- Actual  : a.dat

上一頁12345下一頁
軟件測試工具 | 聯(lián)系我們 | 投訴建議 | 誠聘英才 | 申請使用列表 | 網(wǎng)站地圖
滬ICP備07036474 2003-2017 版權(quán)所有 上海澤眾軟件科技有限公司 Shanghai ZeZhong Software Co.,Ltd