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

這個(gè)結(jié)果告訴我們我們的實(shí)現(xiàn)出現(xiàn)了問題。前面說到, CPPUNIT_ASSERT_EQUAL在兩個(gè)參數(shù)不等時(shí)會(huì)拋出異常,可是這里為什么沒有異常退出了?這是因?yàn),我們?zhí)行每一個(gè)TestCase的 run的時(shí)候,它使用了一種特殊的機(jī)制把函數(shù)包起來,任何異常都會(huì)被捕獲。具體細(xì)節(jié)請(qǐng)參考我的CppUnit代碼簡(jiǎn)介一文。

如果我們把#include "CppUnit/TextOutputter.h"替換成#include "CppUnit/CompilerOutputter.h",并且把TextOutputter替換成CompilerOutputter,輸出變成:

c:unittestunittest.cpp(32) : error : Assertion
Test name:
equality assertion failed
- Expected: a.data
- Actual  : a.dat

Failures !!!
Run: 1   Failure total: 1   Failures: 1   Errors: 0

這個(gè)輸出,在編譯器的信息窗口里面,可以通過雙擊文件名加行號(hào)的那一行來到達(dá)相應(yīng)的位置。

V. 迭代開發(fā)

上面的例子中我們先針對(duì)需求的一部分寫了測(cè)試用例,然后實(shí)現(xiàn)了相應(yīng)的功能。我們可以在這些功能被測(cè)試后,繼續(xù)實(shí)現(xiàn)別的功能的測(cè)試用例,然后繼續(xù)實(shí)現(xiàn)相應(yīng)的功能,這是一個(gè)迭代的過程,我們不斷地增加測(cè)試用例和實(shí)現(xiàn)代碼,后達(dá)成需求。還有一種方法是,先寫好所有的測(cè)試用例(這個(gè)時(shí)候通常會(huì)編譯不通過),然后再添加能夠讓編譯通過所需要的實(shí)現(xiàn)(這個(gè)時(shí)候通常運(yùn)行測(cè)試會(huì)有很多錯(cuò)誤),接著通過正確實(shí)現(xiàn)使得沒有任何測(cè)試錯(cuò)誤,后,對(duì)代碼作優(yōu)化和更新,并且不斷的保證測(cè)試通過。在這里我們著重介紹第二種方法。首先我們先寫下所有的測(cè)試用例,在這里,由于有很多測(cè)試用例,我們不再使用TestCase,因?yàn)門estCase通常用在單一測(cè)試任務(wù)的情況下。這次我們從 TestFixture派生我們的測(cè)試類:

class MyTestCase:public CPPUNIT_NS::TestFixture
{
public:
    void testCtorAndGetName()
    {
        const std::string fileName( "a.dat" );
        FileStatus status( fileName );
        CPPUNIT_ASSERT_EQUAL( status.getFileName(), fileName );
    }
    void testGetFileSize()
    {
        const std::string fileName( "a.dat" );
        FileStatus status( fileName );
        CPPUNIT_ASSERT_EQUAL( status.getFileSize(), 0 );//?
    }
};

寫到這里,我們發(fā)現(xiàn)了兩個(gè)問題,首先我們不停的初始化一些測(cè)試所需的對(duì)象,重復(fù)了很多代碼;其次我們發(fā)現(xiàn)了一個(gè)接口設(shè)計(jì)錯(cuò)誤,我們的接口設(shè)計(jì)上沒有考慮一個(gè)文件不存在的情況。從中可見,先寫好測(cè)試用例,不僅是對(duì)實(shí)現(xiàn)的測(cè)試,也是對(duì)我們?cè)O(shè)計(jì)的測(cè)試。TestFixture定義了兩個(gè)成員函數(shù)setUp和tearDown,在每一個(gè)測(cè)試用例被執(zhí)行的時(shí)候,和它定義在同一個(gè)類內(nèi)部的setUp和tearDown會(huì)被調(diào)用以進(jìn)行初始化和清除工作。我們可以用這兩個(gè)函數(shù)來進(jìn)行統(tǒng)一的初始化代碼。并且,我們修改 getFileSize、setFileModifyDate和getFileModifyDate使得它們?cè)诔霈F(xiàn)錯(cuò)誤的時(shí)候,拋出異常 FileStatusError。下面是我們的測(cè)試用例:

class MyTestCase:public CPPUNIT_NS::TestFixture
{
    std::string mFileNameExist;
    std::string mFileNameNotExist;
    std::string mTestFolder;
    enum DUMMY
    {
        FILE_SIZE = 1011
    };
public:
    virtual void setUp()
    {
        mTestFolder = "c:justfortest";
        mFileNameExist = mTestFolder + "exist.dat";
        mFileNameNotExist = mTestFolder + "notexist.dat";
        if( GetFileAttributes( mTestFolder.c_str() ) != INVALID_FILE_ATTRIBUTES )
            throw std::exception( "test folder already exists" );

        if( ! CreateDirectory( mTestFolder.c_str() ,NULL ) )
            throw std::exception( "cannot create folder" );
        HANDLE file = CreateFile( mFileNameExist.c_str(), GENERIC_READ | GENERIC_WRITE,
            0, NULL, CREATE_NEW, 0, NULL );
        if( file == INVALID_HANDLE_VALUE )
            throw std::exception( "cannot create file" );
        char buffer[FILE_SIZE];
        DWORD bytesWritten;
        if( !WriteFile( file, buffer, FILE_SIZE, &bytesWritten, NULL ) ||
            bytesWritten != FILE_SIZE )
        {
            CloseHandle( file );
            throw std::exception( "cannot write file" );
        }
        CloseHandle( file );
    }
    virtual void tearDown()
    {
        if( ! DeleteFile( mFileNameExist.c_str() ) )
            throw std::exception( "cannot delete file" );
        if( ! RemoveDirectory( mTestFolder.c_str() ) )
            throw std::exception( "cannot remove folder" );
    }
    void testCtorAndGetName()
    {
        FileStatus status( mFileNameExist );
        CPPUNIT_ASSERT_EQUAL( status.getFileName(), mFileNameExist );
    }
    void testGetFileSize()
    {
        FileStatus exist( mFileNameExist );
        //這里FILE_SIZE缺省是int,而getFileSize返回DWORD,不加轉(zhuǎn)換會(huì)導(dǎo)致模版不能正確匹配。
        CPPUNIT_ASSERT_EQUAL( exist.getFileSize(), (DWORD)FILE_SIZE );
        FileStatus notExist( mFileNameNotExist );
        CPPUNIT_ASSERT_THROW( notExist.getFileSize(), FileStatusError );
    }
    void testFileExist()
    {
        FileStatus exist( mFileNameExist );
        CPPUNIT_ASSERT( exist.fileExist() );
        FileStatus notExist( mFileNameNotExist );
        CPPUNIT_ASSERT( ! notExist.fileExist() );
    }
    void testFileModifyDateBasic()
    {
        FILETIME fileTime;
        GetSystemTimeAsFileTime( &fileTime );
        FileStatus exist( mFileNameExist );
        CPPUNIT_ASSERT_NO_THROW( exist.getFileModifyDate() );
        CPPUNIT_ASSERT_NO_THROW( exist.setFileModifyDate( &fileTime ) );
        FileStatus notExist( mFileNameNotExist );
        CPPUNIT_ASSERT_THROW( notExist.getFileModifyDate(), FileStatusError );
        CPPUNIT_ASSERT_THROW( notExist.setFileModifyDate( &fileTime ), FileStatusError );
    }
    void testFileModifyDateEqual()
    {
        FILETIME fileTime;
        GetSystemTimeAsFileTime( &fileTime );
        FileStatus exist( mFileNameExist );
        CPPUNIT_ASSERT_NO_THROW( exist.setFileModifyDate( &fileTime ) );
        FILETIME get = exist.getFileModifyDate();
        // 這里 FILETIME 沒有定義 operator==,所以不能直接使用 CPPUNIT_ASSERT_EQUAL
        CPPUNIT_ASSERT( CompareFileTime( &get, &fileTime ) == 0 );
    }
};

接著我們編寫一個(gè)FileStatus類的骨架,使得這段測(cè)試代碼可以被編譯通過。

class FileStatusError
{};

class FileStatus
{
public:
    FileStatus(const std::string& fileName)
    {}
    DWORD getFileSize() const
    {
        return 0;
    }
    bool fileExist() const
    {
        return false;
    }
    void setFileModifyDate( const FILETIME* )
    {
    }
    FILETIME getFileModifyDate() const
    {
        return FILETIME();
    }
    std::string getFileName() const
    {
        return "";
    }
};

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