BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            1 Posts :: 6 Stories :: 0 Comments :: 0 Trackbacks

          2006年2月22日 #

          如何進行單元測試

          作者:葛志春(來源:希賽網(wǎng))  http://www.csai.cn  2003年06月16日

          摘要:單元測試是軟件測試的基礎,本文詳細的論述了單元測試的兩個步驟人工靜態(tài)檢查法與動態(tài)執(zhí)行跟蹤法,所需執(zhí)行的工作項目及相關的策略和方法。通過對這兩個步驟的描述作者將多年的單元測試經(jīng)驗及測試理論注入于全文。

          關鍵詞:單元測試、人工檢查、白盒測試、測試用例、跟蹤調(diào)試

          1 概述

          單元測試是針對軟件設計的最小單位——程序模塊,進行正確性檢驗的測試工作。其目的在于發(fā)現(xiàn)每個程序模塊內(nèi)部可能存在的差錯。

          單元測試也是程序員的一項基本職責,程序員必須對自己所編寫的代碼保持認真負責的態(tài)度,這是也程序員的基本職業(yè)素質(zhì)之一。同時單元測試能力也是程序員的一項基本能力,能力的高低直接影響到程序員的工作效率與軟件的質(zhì)量。

          在編碼的過程中作單元測試,其花費是最小的,而回報卻特別優(yōu)厚的。在編碼的過程中考慮測試問題,得到的將是更優(yōu)質(zhì)的代碼,因為在這時您對代碼應該做些什么了解得最清楚。如果不這樣做,而是一直等到某個模塊崩潰了,到那時您可能已經(jīng)忘記了代碼是怎樣工作的。即使是在強大的工作壓力下,您也還必須重新把它弄清楚,這又要花費許多時間。進一步說,這樣做出的更正往往不會那么徹底,可能更脆弱,因為您喚回的理解可能不那么完全。

          通常合格的代碼應該具備以下性質(zhì):正確性、清晰性、規(guī)范性、一致性、高效性等(根據(jù)優(yōu)先級別排序)。

          1. 正確性是指代碼邏輯必須正確,能夠?qū)崿F(xiàn)預期的功能。

          2. 清晰性是指代碼必須簡明、易懂,注釋準確沒有歧義。

          3. 規(guī)范性是指代碼必須符合企業(yè)或部門所定義的共同規(guī)范包括命名規(guī)則,代碼風格等等。

          4. 一致性是指代碼必須在命名上(如:相同功能的變量盡量采用相同的標示符)、風格上都保持統(tǒng)一。

          5. 高效性是指代碼不但要滿足以上性質(zhì),而且需要盡可能降低代碼的執(zhí)行時間。

          2 單元測試步驟

          在代碼編寫完成后的單元測試工作主要分為兩個步驟人工靜態(tài)檢查和動態(tài)執(zhí)行跟蹤。

          人工靜態(tài)檢查是測試的第一步,這個階段工作主要是保證代碼算法的邏輯正確性(盡量通過人工檢查發(fā)現(xiàn)代碼的邏輯錯誤)、清晰性、規(guī)范性、一致性、算法高效性。并盡可能的發(fā)現(xiàn)程序中沒有發(fā)現(xiàn)的錯誤。

          第二步是通過設計測試用例,執(zhí)行待測程序來跟蹤比較實際結果與預期結果來發(fā)現(xiàn)錯誤。經(jīng)驗表明,使用人工靜態(tài)檢查法能夠有效的發(fā)現(xiàn)30%到70%的邏輯設計和編碼錯誤。但是代碼中仍會有大量的隱性錯誤無法通過視覺檢查發(fā)現(xiàn),必須通過跟蹤調(diào)試法細心分析才能夠捕捉到。所以,動態(tài)跟蹤調(diào)試方法也成了單元測試的重點與難點。

          3 人工檢查

          通常在人工檢查階段必須執(zhí)行以下項目的活動:

          第一、 檢查算法的邏輯正確性;確定所編寫的代碼算法、數(shù)據(jù)結構定義(如:隊列、堆棧等)是否實現(xiàn)了模塊或方法所要求的功能。

          第二、 模塊接口的正確性檢查;確定形式參數(shù)個數(shù)、數(shù)據(jù)類型、順序是否正確;確定返回值類型及返回值的正確性。

          第三、 輸入?yún)?shù)有沒有作正確性檢查;如果沒有作正確性檢查,確定該參數(shù)是否的確無需做參數(shù)正確性檢查,否則請?zhí)砑由蠀?shù)的正確性檢查。經(jīng)驗表明,缺少參數(shù)正確性檢查的代碼是造成軟件系統(tǒng)不穩(wěn)定的主要原因之一。

          第四、 調(diào)用其他方法接口的正確性;檢查實參類型正確與否、傳入的參數(shù)值正確與否、個數(shù)正確與否,特別是具有多態(tài)的方法。返回值正確與否,有沒有誤解返回值所表示的意思。最好對每個被調(diào)用的方法的返回值用顯濕代碼作正確性檢查,如果被調(diào)用方法出現(xiàn)異常或錯誤程序應該給予反饋,并添加適當?shù)某鲥e處理代碼。

          第五、 出錯處理;模塊代碼要求能預見出錯的條件,并設置適當?shù)某鲥e處理,以便在一旦程序出錯時,能對出錯程序重做安排,保證其邏輯的正確性,這種出錯處理應當是模塊功能的一部分。若出現(xiàn)下列情況之一,則表明模塊的錯誤處理功能包含有錯誤或缺陷:出錯的描述難以理解;出錯的描述不足以對錯誤定位,不足以確定出錯的原因;顯示的錯誤信息與實際的錯誤原因不符;對錯誤條件的處理不正確;在對錯誤進行處理之前,錯誤條件已經(jīng)引起系統(tǒng)的干預等。

          第六、 保證表達式、SQL語句的正確性;檢查所編寫的SQL語句的語法、邏輯的正確性。對表達式應該保證不含二義性,對于容易產(chǎn)生歧義的表達式或運算符優(yōu)先級(如:《 、=、 》、 &&、||、++、 --等)可以采用擴號“()”運算符避免二義性,這樣一方面能夠保證代碼的正確可靠,同時也能夠提高代碼的可讀性。

          第七、 檢查常量或全局變量使用的正確性;確定所使用的常量或全局變量的取值和數(shù)值、數(shù)據(jù)類型;保證常量每次引用同它的取值、數(shù)值和類型的一致性。

          第八、 表示符定義的規(guī)范一致性;保證變量命名能夠見名知意,并且簡潔但不宜過長或過短、規(guī)范、容易記憶、最好能夠拼讀。并盡量保證用相同的表示符代表相同功能,不要將不同的功能用相同的表示符表示;更不要用相同的表示符代表不同的功能意義。

          第九、 程序風格的一致性、規(guī)范性;代碼必須能保證符合企業(yè)規(guī)范,保證所有成員的代碼風格一致、規(guī)范、工整。例如對數(shù)組做循環(huán),不要一會兒采用下標變量從下到上的方式(如:for(I=0;I++;I<10)),一會兒又采用從上到下的方式(如:for(I=10;I--;I>0));應該盡量采用統(tǒng)一的方式,或則統(tǒng)一從下到上,或則統(tǒng)一從上到下。建議采用for循環(huán)和While循環(huán),不要采用do{}while循環(huán)等。

          第十、 檢查程序中使用到的神秘數(shù)字是否采用了表示符定義。神秘的數(shù)字包括各種常數(shù)、數(shù)組的大小、字符位置、變換因子以及程序中出現(xiàn)的其他以文字形式寫出的數(shù)值。在程序源代碼里,一個具有原本形式的數(shù)對其本身的重要性或作用沒提供任何指示性信息,它們也導致程序難以理解和修改。對于這類神秘數(shù)字必須采用相應的標量來表示;如果該數(shù)字在整個系統(tǒng)中都可能使用到務必將它定義為全局常量;如果該神秘數(shù)字在一個類中使用可將其定義為類的屬性(Attribute),如果該神秘數(shù)字只在一個方法中出現(xiàn)務必將其定義為局部變量或常量。

          第十一、 檢查代碼是否可以優(yōu)化、算法效率是否最高。如:SQL語句是否可以優(yōu)化,是否可以用1條SQL語句代替程序中的多條SQL語句的功能,循環(huán)是否必要,循環(huán)中的語句是否可以抽出到循環(huán)之外等。

          第十二、 檢查您的程序是否清晰簡潔容易理解。注意:冗長的程序并不一定不是清晰的。

          第十三、 檢查方法內(nèi)部注釋是否完整;是否清晰簡潔;是否正確的反映了代碼的功能,錯誤的注釋比沒有注釋更糟;是否做了多余的注釋;對于簡單的一看就懂的代碼沒有必要注釋。

          第十四、 檢查注釋文檔是否完整;對包、類、屬性、方法功能、參數(shù)、返回值的注釋是否正確且容易理解;是否會落了或多了某個參數(shù)的注釋,參數(shù)類型是否正確,參數(shù)的限定值是否正確。特別是對于形式參數(shù)與返回值中關于神秘數(shù)值的注釋,如:類型參數(shù) 應該指出 1.代表什么,2.代表什么,3.代表什么等。對于返回結果集(Result Set)的注釋,應該注釋結果集中包含那些字段及字段類型、字段順序等。

          4 動態(tài)執(zhí)行跟蹤

          動態(tài)執(zhí)行測試通常分為黑盒測試與白盒測試。黑盒測試指已知產(chǎn)品的功能設計規(guī)格,可以進行測試證明每個實現(xiàn)了的功能是否符合要求。白盒測試指已知產(chǎn)品的內(nèi)部工作過程,可以通過測試證明每種內(nèi)部操作是否符合設計規(guī)格的要求,所有內(nèi)部成分是否已經(jīng)經(jīng)過檢查。

          對于單元測試來說主要應該采用白盒測試法對每個模塊的內(nèi)部作跟蹤檢查測試。對于單元白盒測試,應該對程序模塊進行如下檢查:

          1. 對模塊內(nèi)所有獨立的執(zhí)行路徑至少測試一次;

          2. 對所有的邏輯判定,取“真”與“假”的兩種情況都至少執(zhí)行一次;

          3. 在循環(huán)的邊界和運行界限內(nèi)執(zhí)行循環(huán)體;

          4. 測試內(nèi)部數(shù)據(jù)的有效性等等。

          單元白盒跟蹤測試,通常需要做如下三項工作:

          1. 設計測試用例;

          2. 設計測試類模塊;

          3. 跟蹤調(diào)試。

          4.1 測試用例設計

          通常動態(tài)執(zhí)行跟蹤調(diào)試是在編碼階段進行的。在對源程序作靜態(tài)人工檢查之后就可以開始進行單元測試的測試用例設計。利用設計文檔,設計可以驗證程序功能、找出程序錯誤的多個測試用例。

          4.1.1 測試用例的設計基本原則

          設計測試用例基本的原則是:

          1. 一個好的測試用例在于能夠發(fā)現(xiàn)至今沒有發(fā)現(xiàn)的錯誤;

          2. 測試用例應由測試輸入數(shù)據(jù)和與之對應的預期輸出結果這兩部分組成;

          3. 在測試用例設計時,應當包含合理的輸入條件和不合理的輸入條件。

          4.1.2 白盒測試的測試用例設計

          白盒測試測試用例一般采用邏輯覆蓋法和基本路徑法進行設計。

          一、邏輯覆蓋法

          邏輯覆蓋是以程序內(nèi)部的邏輯結構為基礎的測試用例設計技術,這一方法要求測試人員對程序的邏輯結構有清楚的了解。邏輯覆蓋可分為:語句覆蓋、判定覆蓋、條件覆蓋、判定-條件覆蓋、條件組合覆蓋與路徑覆蓋。

          1. 語句覆蓋就是設計若干個測試用例,運行所測程序,使得每一可執(zhí)行語句至少執(zhí)行一次。

          2. 判定覆蓋就是設計若干個測試用例,運行所測程序,使得程序中每個判斷的取真分支和取假分支至少經(jīng)歷一次。

          3. 條件覆蓋就是設計若干個測試用例,運行所測程序,使得程序中每個判斷的每個條件的可能取值至少執(zhí)行一次。

          4. 判定--條件覆蓋就是設計足夠的測試用例,使得判斷中每個條件的所有可能取值至少執(zhí)行一次,同時每個判斷的所有可能判斷結果也至少執(zhí)行一次。

          5. 條件組合覆蓋就是設計足夠的測試用例,運行所測程序,使得每個判斷的所有可能的條件取值組合至少執(zhí)行一次。

          6. 路徑測試就是設計足夠的測試用例,覆蓋程序中所有可能的路徑。

          每一種覆蓋方法都有其優(yōu)缺點,這6種覆蓋方法關系,如圖1:



          圖1

          通常在設計測試用例時應該根據(jù)代碼模塊的復雜度,選擇覆蓋方法。一般的代碼的復雜度與測試用例設計的復雜度成正比。因此,設計人員必須做到模塊或方法功能的單一性、高內(nèi)聚性,使得方法或函數(shù)代碼盡可能的簡單;這樣將可大大提高測試用例設計的容易度,提高測試用例的覆蓋程度。

          二、基本路徑法

          基本路徑測試法是在程序控制流圖的基礎上,通過分析控制構造的環(huán)路復雜性,導出基本可執(zhí)行路徑集合,從而設計測試用例的方法。設計出的測試用例要保證在測試中程序的每個可執(zhí)行語句至少執(zhí)行一次。基本路徑測試法包括以下5個方面:

          1. 程序的控制流圖:描述程序控制流的一種圖示方法。

          2. 程序環(huán)境復雜性:McCabe復雜性度量;從程序的環(huán)路復雜性可導出程序基本路徑集合中的獨立路徑條數(shù),這是確定程序中每個可執(zhí)行語句至少執(zhí)行依次所必須的測試用例數(shù)目的上界。

          3. 導出測試用例。

          4. 準備測試用例,確保基本路徑集中的每一條路徑的執(zhí)行。

          5. 圖形矩陣:是在基本路徑測試中起輔助作用的軟件工具,利用它可以實現(xiàn)自動地確定一個基本路徑集。

          另外,對于測試用例的選擇除了滿足所選擇的覆蓋程度(或覆蓋標準)外還需要盡可能的采用邊界值分析法、錯誤推測法等常用地設計方法。采用邊界值分析法設計合理的輸入條件與不合理的輸入條件;條件邊界測試用例應該包括輸入?yún)?shù)的邊界與條件邊界(if,while,for,switch ,SQL Where子句等)。錯誤推測法,列舉出程序中所有可能的錯誤和容易發(fā)生錯誤的特殊情況,根據(jù)它們選擇測試用例;在編碼、單元測試階段可以發(fā)現(xiàn)很多常見的錯誤和疑似錯誤,對于這些錯誤應該作重點測試,并設計相應的測試用例。

          4.1.3 單元測試計劃表格

          在設計測試用例時可以參考如下表格,擬定對每個類(或模塊或包)的測試計劃。表1,是對每個類(或模塊或包)作測試計劃的表頭,它指明本測試計劃是針對那個模塊及相關文件的。表2是針對表1指定模塊測試用例而對應的子表,每個測試用例可以擁有一個子表;單元測試結果子表留作執(zhí)行測試用例時根據(jù)實際結果填寫。

          子系統(tǒng)名. PackageName. JavaClassName

          單元測試計劃
          標識 格式:

          “子系統(tǒng)名. jsp_filename(含目錄中間用\分開即可)”

          或者

          “子系統(tǒng)名. PackageName. JavaClassName”
          組件功能項 如:組件完成 “新增貼子”的功能
          針對概要/詳細設計文件名 如:1.1版本公告部分詳細設計說明書
          物理文件名 jsp_filename(含目錄);

          packageName. JavaClassName
          表1

          單元測試子項001

          下面表格為針對上面表格“子系統(tǒng)名. PackageName. JavaClassName”而對應的子表,每個測試用例用一張子表:

          編號 .001 注:“. 編號” 部分要從001編號開始一直到999,個人自行編號
          程序設計人員 如:葛志春
          測試人員 如:葛志春
          測試目的 如:對錯誤邏輯輸入檢驗
          測試內(nèi)容描述 如:對于public int fun3(String p1, int p2 )的輸入檢驗,如果 p1 == null ,程序中檢驗到,應該記錄到系統(tǒng) logfile, return –1;
          輸入期望 P1 == null
          功能處理期望描述 Logfile 多一條歷史記錄,方法return -1;
          輸出期望 Return –1
          單元測試結果
          實際輸入數(shù)據(jù) P1 = null
          實際處理情況描述 程序沒有進行p1 == null 的驗證,沒有及時return –1,而是運行到 p1.aaa( ) 方法時出現(xiàn) null pointer 異常。
          實際輸出 沒有寫 logfile 文件;
          測試結論 正常 / 異常
          表2

          4.2 測試類設計

          一個模塊或一個方法(Method)并不是一個獨立的程序,在考慮測試它時要同時考慮它和外界的聯(lián)系,用些輔助模塊去模擬與所測模塊相聯(lián)系的其他模塊。這些輔助模塊分為兩種:

          1. 驅(qū)動模塊(driver):相當于所測模塊的主程序。它接收測試數(shù)據(jù),把這些數(shù)據(jù)傳送給所測模塊,最后再輸出實際測試結果。

          2. 樁模塊(stub):用于代替所測模塊調(diào)用的子模塊。樁模塊可以做少量的數(shù)據(jù)操作,不需要把子模塊所有功能都帶進來,但不容許什么事情也不做。

          所測模塊與它相關的驅(qū)動模塊及樁模塊共同構成了一個“測試環(huán)境”,如圖2。驅(qū)動模塊和樁模塊的編寫會給測試帶來額外的開銷。因為它們在軟件交付時不作為產(chǎn)品的一部分一同交付,而且它們的編寫需要一定的工作量。特別是樁模塊,不能只簡單地給出“曾經(jīng)進入”的信息。為了能夠正確的測試軟件,樁模塊可能需要模擬實際子模塊的功能,這樣樁模塊的建立就不是很輕松了。




          圖2 單元測試的測試環(huán)境

          編寫樁模塊是困難費時的,其實也是完全可以避免編寫樁模塊的;只需在項目進度管理時將實際樁模塊的代碼編寫工作安排在被測模塊前編寫即可。而且這樣可以提高測試工作的效率,提高實際樁模塊的測試頻率從而更有效的保證產(chǎn)品的質(zhì)量。但是,為了保證能夠向上一層級提供穩(wěn)定可靠的實際樁模塊,為后續(xù)模塊測試打下良好的基礎,驅(qū)動模塊還是必不可少的。

          對于每一個包或子系統(tǒng)我們可以根據(jù)所編寫的測試用例來編寫一個測試模塊類來做驅(qū)動模塊,用于測試包中所有的待測試模塊。而最好不要在每個類中用一個測試函數(shù)的方法,來測試跟蹤類中所有的方法。這樣的好處在于:

          1. 能夠同時測試包中所有的方法或模塊,也可以方便的測試跟蹤指定的模塊或方法。

          2. 能夠聯(lián)合使用所有測試用例對同一段代碼執(zhí)行測試,發(fā)現(xiàn)問題。

          3. 便以回歸測試,當某個模塊作了修改之后,只要執(zhí)行測試類就可以執(zhí)行所有被測的模塊或方法。這樣不但能夠方便得檢查、跟蹤所修改的代碼,而且能夠檢查出修改對包內(nèi)相關模塊或方法所造成的影響,使修改引進的錯誤得以及時發(fā)現(xiàn)。

          4. 復用測試方法,使測試單元保持持久性,并可以用既有的測試來編寫相關測試。

          5. 將測試代碼與產(chǎn)品代碼分開,使代碼更清晰、簡潔;提高測試代碼與被測代碼的可維護性。

          4.3 跟蹤調(diào)試

          跟蹤調(diào)試不但是深入測試代碼的最佳方法,而且也是程序調(diào)試發(fā)現(xiàn)錯誤根源的有利工具。

          測試類設計完成后,最好能借助代碼排錯工具來跟蹤調(diào)試待測代碼段以深入的檢查代碼的邏輯錯誤。現(xiàn)有的代碼開發(fā)工具(如:JBuilder)一般都集成了這類排錯工具。排錯工具一般由執(zhí)行控制程序、執(zhí)行狀態(tài)查詢程序、跟蹤程序組成。執(zhí)行控制程序包括斷點定義、斷點撤銷、單步執(zhí)行、斷點執(zhí)行、條件執(zhí)行等功能。執(zhí)行狀態(tài)查詢程序包括寄存器、堆棧狀態(tài)、變量、代碼等與程序相關的各種狀態(tài)信息的查詢。跟蹤程序用以跟蹤程序執(zhí)行過程中所經(jīng)歷的事件序列(如:分支、子程序調(diào)用等)。程序員可通過對程序執(zhí)行過程中各種狀態(tài)的判別進行程序錯誤的識別、定位及改正。

          對于模塊的單元跟蹤調(diào)試,最好能夠做到對被測模塊的每次修改,都對每個測試用例進行跟蹤執(zhí)行一遍以排除所有可能出現(xiàn)或引進的錯誤。在時間有限的情況下也必須調(diào)用驅(qū)動模塊對所有的測試用例執(zhí)行一次,并對出現(xiàn)錯誤或異常的測試用例跟蹤執(zhí)行一次,以發(fā)現(xiàn)問題的根源。

          排錯過程往往是一個艱苦的過程,特別是那種算法復雜、調(diào)用子模塊較多的模塊,對于錯誤的定位來說并不是一件容易的事情。盡管排錯不是一門好學的技術(有時人們更愿意稱之為藝術),但還是有若干行之有效的方法和策略,下面介紹幾種排錯時應該采用的方法策略。

          1. 斷點設置,設置斷點對源程序?qū)嵭袛帱c跟蹤將能夠大大提高排錯的效率。通常斷點的設置除了根據(jù)經(jīng)驗與錯誤信息來設置外,還應重點考慮以下幾種類行的語句。

          1) 函數(shù)調(diào)用語句。子函數(shù)的調(diào)用語句是測試的重點,一方面由于在調(diào)用子函數(shù)時可能引起接口引用錯誤,另一方面可能是子函數(shù)本身的錯誤。

          2) 判定轉(zhuǎn)移/循環(huán)語句。判定語句常常會由于邊界值與比較優(yōu)先級等問題引起錯誤或失效而作出錯誤的轉(zhuǎn)移。因此,對于判定轉(zhuǎn)移/循環(huán)語句也是一個重要的測試點。

          3) SQL語句。對于數(shù)據(jù)庫的應用程序來說,SQL語句常常會在模塊中占比較重要的業(yè)務邏輯,而且比較復雜。因此,它也屬于比較容易出現(xiàn)錯誤的語句。

          4) 復雜算法段。出錯的概率常與算法的復雜度成正比。所以越復雜的算法越需要作重點跟蹤,如遞歸、回朔等算法。

          2. 可疑變量查看,在跟蹤執(zhí)行狀態(tài)下當程序停止在某條語句時可以查看變量的當前值和對象的當前屬性。通過對比這些變量當前值與預期值可以輕松的定位程序問題根源。

          3. SQL語句執(zhí)行檢查,在跟蹤執(zhí)行或運行狀態(tài)下將疑似錯誤的SQL語句打印出來,重新在數(shù)據(jù)庫SQL查詢分析器(如:Oracle SQL Plus)中跟蹤執(zhí)行可以較高效的檢查糾正SQL語句錯誤。

          4. 注意群集現(xiàn)象,經(jīng)驗表明測試后程序中殘存的錯誤數(shù)目與該程序中已發(fā)現(xiàn)的錯誤數(shù)目或檢錯率成正比。根據(jù)這個規(guī)律,應當對錯誤群集的程序段進行重點測試,以提高測試投資的效益。如果發(fā)現(xiàn)某一代碼段似乎比其他程序模塊更多的錯誤傾向時,則應當花費較多的時間和代價測試這個程序模塊。
          posted @ 2006-02-22 14:58 阿獸學習 閱讀(219) | 評論 (0)編輯 收藏

          僅列出標題  
          主站蜘蛛池模板: 和田县| 山阳县| 曲阳县| 甘谷县| 共和县| 珠海市| 苏尼特左旗| 洛宁县| 光泽县| 恩平市| 锡林郭勒盟| 台山市| 玉溪市| 永清县| 保康县| 株洲县| 思南县| 汶川县| 达孜县| 肥乡县| 三河市| 枣强县| 盐津县| 台山市| 云和县| 开化县| 汉沽区| 麻栗坡县| 博兴县| 曲水县| 金坛市| 搜索| 始兴县| 屯门区| 临颍县| 海南省| 玉门市| 赤峰市| 新干县| 章丘市| 富源县|