<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    LALA  
    日歷
    <2009年6月>
    31123456
    78910111213
    14151617181920
    21222324252627
    2829301234
    567891011

    導航

    留言簿(1)

    隨筆分類(31)

    文章分類(4)

    收藏夾(21)

    搜索

    •  

    積分與排名

    • 積分 - 29818
    • 排名 - 1390

    最新隨筆

    最新評論

    閱讀排行榜

     
    單元測試
       單元測試(模塊測試)是開發(fā)者編寫的一小段代碼,用于檢驗被測代碼的一個很小的、很明確的功能是否正確。通常而言,一個單元測試是用于判斷某個特定條件(或者場景)下某個特定函數(shù)的行為。例如,你可能把一個很大的值放入一個有序list 中去,然后確認該值出現(xiàn)在list 的尾部。或者,你可能會從字符串中刪除匹配某種模式的字符,然后確認字符串確實不再包含這些字符了。

        單元測試是由程序員自己來完成,最終受益的也是程序員自己。可以這么說,程序員有責任編寫功能代碼,同時也就有責任為自己的代碼編寫單元測試。執(zhí)行單元測試,就是為了證明這段代碼的行為和我們期望的一致。

        要進行充分的單元測試,應(yīng)專門編寫測試代碼,并與產(chǎn)品代碼隔離。個人認為,比較簡單的辦法是為產(chǎn)品工程建立對應(yīng)的測試工程,為每個類建立對應(yīng)的測試類,為每個函數(shù)(很簡單的除外)建立測試函數(shù)。首先就幾個概念談?wù)剛€人的看法。

        一般認為,在結(jié)構(gòu)化程序時代,單元測試所說的單元是指函數(shù),在當今的面向?qū)ο髸r代,單元測試所說的單元是指類。以個人的實踐來看,以類作為測試單位,復雜度高,可操作性較差,因此仍然主張以函數(shù)作為單元測試的測試單位,但可以用一個測試類來組織某個類的所有測試函數(shù)。單元測試不應(yīng)過分強調(diào)面向?qū)ο螅驗榫植看a依然是結(jié)構(gòu)化的。單元測試的工作量較大,簡單實用高效才是硬道理。

        有一種看法是,只測試類的接口(公有函數(shù)),不測試其他函數(shù),從面向?qū)ο蠼嵌葋砜矗_實有其道理,但是,測試的目的是找錯并最終排錯,因此,只要是包含錯誤的可能性較大的函數(shù)都要測試,跟函數(shù)是否私有沒有關(guān)系。對于C++來說,可以用一種簡單的方法區(qū)隔需測試的函數(shù):簡單的函數(shù)如數(shù)據(jù)讀寫函數(shù)的實現(xiàn)在頭文件中編寫(inline函數(shù)),所有在源文件編寫實現(xiàn)的函數(shù)都要進行測試(構(gòu)造函數(shù)和析構(gòu)函數(shù)除外)。

    測試代碼編寫
       數(shù)講述單元測試的文章都是以Java為例,本文以C++為例,后半部分所介紹的單元測試工具也只介紹C++單元測試工具。下面的示例代碼的開發(fā)環(huán)境是VC6.0。

       產(chǎn)品類:
    class CMyClass
    {
    public:
        
    int Add(int i, int j);
         CMyClass();
        
    virtual ~CMyClass();

    private:
        
    int mAge; //年齡 
        
    CString mPhase; //年齡階段,如"少年","青年"
    };


       建立對應(yīng)的測試類CMyClassTester,為了節(jié)約編幅,只列出源文件的代碼:
    void CMyClassTester::CaseBegin()
    {
        
    //pObj是CMyClassTester類的成員變量,是被測試類的對象的指針,
        
    //為求簡單,所有的測試類都可以用pObj命名被測試對象的指針。
        
    pObj = new CMyClass();
    }

    void CMyClassTester::CaseEnd()
    {
         delete pObj;
    }

       測試類的函數(shù)CaseBegin()和CaseEnd()建立和銷毀被測試對象,每個測試用例的開頭都要調(diào)用CaseBegin(),結(jié)尾都要調(diào)用CaseEnd()。

       接下來,我們建立示例的產(chǎn)品函數(shù):
    int CMyClass::Add(int i, int j)
    {
        
    return i+j;
    }

       和對應(yīng)的測試函數(shù):
    void CMyClassTester::Add_int_int()
    {
    }

       把參數(shù)表作為函數(shù)名的一部分,這樣當出現(xiàn)重載的被測試函數(shù)時,測試函數(shù)不會產(chǎn)生命名沖突。下面添加測試用例:
    void CMyClassTester::Add_int_int()
    {
       
    //第一個測試用例
       CaseBegin();{ //1
       int i = 0//2
       int j = 0//3
       int ret = pObj->Add(i, j); //4
       ASSERT(ret == 0); //5
       }CaseEnd(); //6
    }

        第1和第6行建立和銷毀被測試對象,所加的{}是為了讓每個測試用例的代碼有一個獨立的域,以便多個測試用例使用相同的變量名。

        第2和第3行是定義輸入數(shù)據(jù),第4行是調(diào)用被測試函數(shù),這些容易理解,不作進一步解釋。第5行是預(yù)期輸出,它的特點是當實際輸出與預(yù)期輸出不同時自動報錯,ASSERT是VC的斷言宏,也可以使用其他類似功能的宏,使用測試工具進行單元測試時,可以使用該工具定義的斷言宏。

    示例中的格式顯得很不簡潔,2、3、4、5行可以合寫為一行:ASSERT(pObj->Add(0, 0) == 0);但這種不簡潔的格式卻是個人極力推薦的,因為它一目了然,易于建立多個測試用例,并且具有很好的適應(yīng)性,同時,也是極佳的代碼文檔,總之,個人建議:輸入數(shù)據(jù)和預(yù)期輸出要自成一塊。

    建立了第一個測試用例后,應(yīng)編譯并運行測試,以排除語法錯誤,然后,使用拷貝/修改的辦法建立其他測試用例。由于各個測試用例之間的差別往往很小,通常只需修改一兩個數(shù)據(jù),拷貝/修改是建立多個測試用例的最快捷辦法。

    測試用例
       下面說說測試用例、輸入數(shù)據(jù)及預(yù)期輸出。輸入數(shù)據(jù)是測試用例的核心,個人對輸入數(shù)據(jù)的定義是:被測試函數(shù)所讀取的外部數(shù)據(jù)及這些數(shù)據(jù)的初始值。外部數(shù)據(jù)是對于被測試函數(shù)來說的,實際上就是除了局部變量以外的其他數(shù)據(jù),個人把這些數(shù)據(jù)分為幾類:參數(shù)、成員變量、全局變量、IO媒體。IO媒體是指文件、數(shù)據(jù)庫或其他儲存或傳輸數(shù)據(jù)的媒體,例如,被測試函數(shù)要從文件或數(shù)據(jù)庫讀取數(shù)據(jù),那么,文件或數(shù)據(jù)庫中的原始數(shù)據(jù)也屬于輸入數(shù)據(jù)。一個函數(shù)無論多復雜,都無非是對這幾類數(shù)據(jù)的讀取、計算和寫入。預(yù)期輸出是指:返回值及被測試函數(shù)所寫入的外部數(shù)據(jù)的結(jié)果值。返回值就不用說了,被測試函數(shù)進行了寫操作的參數(shù)(輸出參數(shù))、成員變量、全局變量、IO媒體,它們的預(yù)期的結(jié)果值都是預(yù)期輸出。一個測試用例,就是設(shè)定輸入數(shù)據(jù),運行被測試函數(shù),然后判斷實際輸出是否符合預(yù)期。下面舉一個與成員變量有關(guān)的例子:
    產(chǎn)品函數(shù):
    void CMyClass::Grow(int years)
    {
        mAge 
    += years;

        
    if(mAge < 10)
            mPhase 
    = "兒童";
        
    else if(mAge <20)
            mPhase 
    = "少年";
        
    else if(mAge <45)
            mPhase 
    = "青年";
        
    else if(mAge <60)
            mPhase 
    = "中年";
        
    else
            mPhase 
    = "老年";
    }

       測試函數(shù)中的一個測試用例:
        CaseBegin();{
        
    int years = 1;
        pObj
    ->mAge = 8;
        pObj
    ->Grow(years);
        ASSERT( pObj
    ->mAge == 9 );
        ASSERT( pObj
    ->mPhase == "兒童" );
        }CaseEnd();

       在輸入數(shù)據(jù)中對被測試類的成員變量mAge進行賦值,在預(yù)期輸出中斷言成員變量的值。現(xiàn)在可以看到個人所推薦的格式的好處了吧,這種格式可以適應(yīng)很復雜的測試。在輸入數(shù)據(jù)部分還可以調(diào)用其他成員函數(shù),例如:執(zhí)行被測試函數(shù)前可能需要讀取文件中的數(shù)據(jù)保存到成員變量,或需要連接數(shù)據(jù)庫,個人把這些操作稱為初始化操作。例如,上例中 ASSERT( ...)之前可以加pObj->OpenFile();。為了訪問私有成員,可以將測試類定義為產(chǎn)品類的友元類。例如,定義一個宏:
    #define UNIT_TEST(cls) friend class cls##Tester;

       然后在產(chǎn)品類聲明中加一行代碼:
    UNIT_TEST(ClassName)


       下面談?wù)劀y試用例設(shè)計。前面已經(jīng)說了,測試用例的核心是輸入數(shù)據(jù)。預(yù)期輸出是依據(jù)輸入數(shù)據(jù)和程序功能來確定的,也就是說,對于某一程序,輸入數(shù)據(jù)確定了,預(yù)期輸出也就可以確定了,至于生成/銷毀被測試對象和運行測試的語句,是所有測試用例都大同小異的,因此,我們討論測試用例時,只討論輸入數(shù)據(jù)。

       前面說過,輸入數(shù)據(jù)包括四類:參數(shù)、成員變量、全局變量、IO媒體,這四類數(shù)據(jù)中,只要所測試的程序需要執(zhí)行讀操作的,就要設(shè)定其初始值,其中,前兩類比較常用,后兩類較少用。顯然,把輸入數(shù)據(jù)的所有可能取值都進行測試,是不可能也是無意義的,我們應(yīng)該用一定的規(guī)則選擇有代表性的數(shù)據(jù)作為輸入數(shù)據(jù),主要有三種:正常輸入,邊界輸入,非法輸入,每種輸入還可以分類,也就是平常說的等價類法,每類取一個數(shù)據(jù)作為輸入數(shù)據(jù),如果測試通過,可以肯定同類的其他輸入也是可以通過的。下面舉例說明:
    • 正常輸入: 例如字符串的Trim函數(shù),功能是將字符串前后的空格去除,那么正常的輸入可以有四類:前面有空格;后面有空格;前后均有空格;前后均無空格。
    • 邊界輸入: 上例中空字符串可以看作是邊界輸入;再如一個表示年齡的參數(shù),它的有效范圍是0-100,那么邊界輸入有兩個:0和100。
    • 非法輸入: 非法輸入是正常取值范圍以外的數(shù)據(jù),或使代碼不能完成正常功能的輸入,如上例中表示年齡的參數(shù),小于0或大于100都是非法輸入,再如一個進行文件操作的函數(shù),非法輸入有這么幾類:文件不存在;目錄不存在;文件正在被其他程序打開;權(quán)限錯誤。

       如果函數(shù)使用了外部數(shù)據(jù),則正常輸入是肯定會有的,而邊界輸入和非法輸入不是所有函數(shù)都有。一般情況下,即使沒有設(shè)計文檔,考慮以上三種輸入也可以找出函數(shù)的基本功能點。實際上,單元測試與代碼編寫是“一體兩面”的關(guān)系,編碼時對上述三種輸入都是必須考慮的,否則代碼的健壯性就會成問題。

    白盒覆蓋
       上面所說的測試數(shù)據(jù)都是針對程序的功能來設(shè)計的,就是所謂的黑盒測試。單元測試還需要從另一個角度來設(shè)計測試數(shù)據(jù),即針對程序的邏輯結(jié)構(gòu)來設(shè)計測試用例,就是所謂的白盒測試。在個人看來,如果黑盒測試是足夠充分的,那么白盒測試就沒有必要,可惜“足夠充分”只是一種理想狀態(tài),例如:真的是所有功能點都測試了嗎?程序的功能點是人為的定義,常常是不全面的;各個輸入數(shù)據(jù)之間,有些組合可能會產(chǎn)生問題,怎樣保證這些組合都經(jīng)過了測試?難于衡量測試的完整性是黑盒測試的主要缺陷,而白盒測試恰恰具有易于衡量測試完整性的優(yōu)點,兩者之間具有極好的互補性,例如:完成功能測試后統(tǒng)計語句覆蓋率,如果語句覆蓋未完成,很可能是未覆蓋的語句所對應(yīng)的功能點未測試。

       白盒測試針對程序的邏輯結(jié)構(gòu)設(shè)計測試用例,用邏輯覆蓋率來衡量測試的完整性。邏輯單位主要有:語句、分支、條件、條件值、條件值組合,路徑。語句覆蓋就是覆蓋所有的語句,其他類推。另外還有一種判定條件覆蓋,其實是分支覆蓋與條件覆蓋的組合,在此不作討論。跟條件有關(guān)的覆蓋就有三種,解釋一下:條件覆蓋是指覆蓋所有的條件表達式,即所有的條件表達式都至少計算一次,不考慮計算結(jié)果;條件值覆蓋是指覆蓋條件的所有可能取值,即每個條件的取真值和取假值都要至少計算一次;條件值組合覆蓋是指覆蓋所有條件取值的所有可能組合。個人做過一些粗淺的研究,發(fā)現(xiàn)與條件直接有關(guān)的錯誤主要是邏輯操作符錯誤,例如:||寫成&&,漏了寫!什么的,采用分支覆蓋與條件覆蓋的組合,基本上可以發(fā)現(xiàn)這些錯誤,另一方面,條件值覆蓋與條件值組合覆蓋往往需要大量的測試用例,因此,在個人看來,條件值覆蓋和條件值組合覆蓋的效費比偏低。個人認為效費比較高且完整性也足夠的測試要求是這樣的:完成功能測試,完成語句覆蓋、條件覆蓋、分支覆蓋、路徑覆蓋。

       關(guān)于白盒測試用例的設(shè)計,程序測試領(lǐng)域的書籍一般都有講述,普通方法是畫出程序的邏輯結(jié)構(gòu)圖如程序流程圖或控制流圖,根據(jù)邏輯結(jié)構(gòu)圖設(shè)計測試用例,這些是純粹的白盒測試,不是個人想推薦的方式。個人所推薦的方法是:先完成黑盒測試,然后統(tǒng)計白盒覆蓋率,針對未覆蓋的邏輯單位設(shè)計測試用例覆蓋它,例如,先檢查是否有語句未覆蓋,有的話設(shè)計測試用例覆蓋它,然后用同樣方法完成條件覆蓋、分支覆蓋和路徑覆蓋,這樣的話,既檢驗了黑盒測試的完整性,又避免了重復的工作,用較少的時間成本達到非常高的測試完整性。不過,這些工作可不是手工能完成的,必須借助于工具,后面會介紹可以完成這些工作的測試工具。

    單元測試工具
       CppUnit,這是C++單元測試工具的鼻祖,免費的開源的單元測試框架。由于已有一眾高人寫了不少關(guān)于CppUnit的很好的文章,個人就不現(xiàn)丑了,想了解CppUnit的朋友,建議讀一下Cpluser 所作的《CppUnit測試框架入門》,網(wǎng)址是:http://blog.csdn.net/cpluser/archive/2004/09/21/111522.aspx。該文也提供了CppUnit的下載地址。
    posted on 2009-06-17 22:08 Dest 閱讀(327) 評論(0)  編輯  收藏 所屬分類: 軟件工程
     
    Copyright © Dest Powered by: 博客園 模板提供:滬江博客
    主站蜘蛛池模板: 亚洲国产成人久久精品动漫| 亚洲福利精品电影在线观看| 亚洲综合婷婷久久| 国产真人无码作爱视频免费| 亚洲国产精彩中文乱码AV| 两个人看的www高清免费视频| 无码欧精品亚洲日韩一区夜夜嗨| 亚洲AV无码国产精品永久一区| 老司机永久免费网站在线观看| 亚洲AV网一区二区三区| 国产一级淫片免费播放电影| 色吊丝免费观看网站| 久久久青草青青国产亚洲免观| 中文字幕免费视频精品一| 亚洲av一综合av一区| 亚洲第一网站免费视频| 亚洲性色AV日韩在线观看| 日韩中文字幕免费| 国产精品视频全国免费观看| 亚洲午夜国产精品无码 | 国产V片在线播放免费无码| 久久久久亚洲AV无码专区桃色| 免费人成网站在线观看不卡| 亚洲日本乱码一区二区在线二产线| 青春禁区视频在线观看直播免费| 亚洲精品自偷自拍无码| 亚洲一区无码精品色| 热re99久久6国产精品免费| 亚洲熟女综合一区二区三区| 亚洲国产成人精品久久久国产成人一区二区三区综 | 亚洲国产成人精品久久久国产成人一区二区三区综 | 中文字幕av免费专区| 久久精品亚洲一区二区三区浴池 | 亚洲欧洲国产成人综合在线观看| 中文字幕在线免费观看视频| 亚洲日本在线免费观看| 亚洲AV无码乱码在线观看牲色| 久久久免费的精品| 亚洲av中文无码字幕色不卡| 亚洲精品狼友在线播放| 女人18毛片a级毛片免费视频|