經過系統的幾個月測試,對測試有一定的瞭解,發現單元測試是測試一個非常重要的一部分,於是對單元測試發生了興趣,通過閱讀資料、實踐。對單元測試進行了一些總結,希望能夠對大家有些協助,得到拋磚引玉的作用。
1. 什麼是單元測試
單元測試是開發人員編寫的一小段代碼,用於檢驗被測代碼的一個很小的、很明確的功能是否正確。通常而言,一個單元測試是用於判斷某個特定條件(或者情境)下某個特定函數的行為。例如,你可能把一個很大的值放入一個有序list 中去,然後確認該值出現在list 的尾部。或者,你可能會從字串中刪除匹配某種模式的字元,然後確認字串確實不再包含這些字元了。
2. 為什麼要使用單元測試
編寫代碼時,一定會反覆調試保證它能夠編譯通過。如果是編譯沒有通過的代碼,沒有任何人會願意交付給自己的老闆。但代碼通過編譯,只是說明了它的文法正確;卻無法保證它的語義也一定正確,沒有任何人可以輕易承諾這段代碼的行為一定是正確的。
幸運,單元測試會為我們的承諾做保證。編寫單元測試就是用來驗證這段代碼的行為是否與我們期望的一致。有了單元測試,我們可以自信的交付自己的代碼,而沒有任何的後顧之憂。但我們對單元測試也有一些認識的誤區,誤區有哪些呢?
(1)、 編寫單元測試太花時間了
在開發時越早發現BUG,就能節省更多的時間,降低更多的風險。如果不及時修改錯誤,一後期發現的錯誤,調試修改很困難,更浪費時間;二是維護越多,代碼的結構越亂,甚至改變當初的實際思路。
(2)、 運行測試的時間太長了
合適的測試是不會讓這種情況發生的。實際上,大多數測試的執行都是非常快的,因此你在幾秒之內就可以運行成千上萬個測試。但是有時某些測試會花費很長的時間。這時,需要把這些耗時的測試和其他測試分開。通常可以每天運行這種測試一次,或者幾天一次。
(3)、 測試代碼並不是我的工作
工作就是保證代碼能夠正確的完成,恰恰相反,測試代碼是不可缺少的工作。
(4)、 不清楚代碼的行為,所以也就無從測試
如果實在不清楚代碼的行為,那麼估計現在並不是編碼的時候。如果並不知道代碼的行為,那麼你又如何知道你編寫的代碼是正確的呢?
(5)、 但是這些代碼都能夠編譯通過
代碼通過編譯只是驗證它的文法通過,但並不能保證它的行為就一定正確。
(6)、 項目進度吃緊時少做些測試,時間富裕時多做測試
這是不重視軟體測試的表現,也是軟體項目過程管理混亂的表現,必然會降低軟體測試的品質。一個軟體項目的順利實現需要有合理的項目進度計劃,其中包括合理的測試計劃,對項目實施過程中的任何問題,都要有風險分析和相應的對策,不要因為開發進度的延期而簡單的縮短測試時間、人力和資源。因為縮短測試時間帶來的測試不完整,對項目品質的下降引起的潛在風險,往往造成更大的浪費。克服這種現象的最好辦法是加強軟體過程的計劃和控制,包括軟體測試計劃、測試設計、測試執行、測試度量和測試控制。
(7)、 軟體測試是沒有前途的工作,只有程式員才是軟體高手
項目的成功往往靠個別全能程式員決定,他們負責總體設計和程式詳細設計,認為軟體開發就是編寫代碼,給人的印象往往是程式員是真正的牛人,具有很高的地位和待遇。因此,在這種環境下,軟體測試很不受重視,軟體測試人員的地位和待遇自然就很低了,甚至軟體測試變得可有可無。隨著市場對軟體品質的不斷提高,軟體測試將變得越來越重要,相應的軟體測試人員的地位和待遇將會逐漸提高。在微軟等軟體過程比較規範的大公司,軟體測試人員的數量和待遇與程式員沒有多大差別,優秀測試人員的待遇甚至比程式員還要高。軟體測試將會成為一個具有很大發展前景的行業,軟體測試大有前途,市場需要更多具有豐富測試技術和管理經驗的測試人員,他們同樣是軟體專家。
3. 單元測試有哪些優點
(1)、它是一種驗證行為。
程式中的每一項功能都是測試來驗證它的正確性。它為以後的開發提供支緩。就算是開發後期,我們也可以輕鬆的增加功能或更改程式結構,而不用擔心這個過程中會破壞重要的東西。而且它為代碼的重構提供了保障。這樣,我們就可以更自由的對程式進行改進。
(2)、它是一種設計行為。
編寫單元測試將使我們從調用者觀察、思考。特別是先寫測試(test-first),迫使我們把程式設計成易於調用和可測試的,即迫使我們解除軟體中的耦合。
(3)、它是一種編寫文檔的行為。
單元測試是一種無價的文檔,它是展示函數或類如何使用的最佳文檔。這份文檔是可編譯、可啟動並執行,並且它保持最新,永遠與代碼同步。
(4)、它具有迴歸性。
自動化的單元測試避免了代碼出現迴歸,編寫完成之後,可以隨時隨地的快速運行測試。
4. 什麼時候進行單元測試
單元測試越早越好,早到什麼程度?XP開發理論講究TDD,即測試驅動開發,先編寫測試代碼,再進行開發。在實際的工作中,可以不必過分強調先什麼後什麼,重要的是高效和感覺舒適。從經驗來看,先編寫產品函數的架構,然後編寫測試函數,針對產品函數的功能編寫測試案例,然後編寫產品函數的代碼,每寫一個功能點都運行測試,隨時補充測試案例。所謂先編寫產品函數的架構,是指先編寫函數空的實現,有傳回值的隨便返回一個值,編譯通過後再編寫測試代碼,這時,函數名、參數表、傳回型別都應該確定下來了,所編寫的測試代碼以後需修改的可能性比較小。
5. 由誰來進行單元測試
單元測試與其他測試不同,單元測試可看作是編碼工作的一部分,應該由程式員完成,也就是說,經過了單元測試的代碼才是已完成的代碼,提交產品代碼時也要同時提交測試代碼。測試部門可以作一定程度的審核。
6. 單元測試的任務
單元測試任務包括:
(1) 模組介面測試
模組介面測試是單元測試的基礎。只有在資料能正確流入、流出模組的前提下,其他測試才有意義。測試介面正確與否應該考慮下列因素:
輸入的實際參數與形式參數的個數是否相同;
輸入的實際參數與形式參數的屬性是否匹配;
輸入的實際參數與形式參數的量綱是否一致;
調用其他模組時所給實際參數的個數是否與被調模組的形參個數相同;
調用其他模組時所給實際參數的屬性是否與被調模組的形參屬性匹配;
調用其他模組時所給實際參數的量綱是否與被調模組的形參量綱一致;
調用預定義函數時所用參數的個數、屬性和次序是否正確;
是否存在與當前進入點無關的參數引用;
是否修改了唯讀型參數;
對全程變數的定義各模組是否一致;
是否把某些約束作為參數傳遞。
如果模組內包括外部輸入輸出,還應該考慮下列因素:
檔案屬性是否正確;
OPEN/CLOSE語句是否正確;
格式說明與輸入輸出語句是否匹配;
緩衝區大小與記錄長度是否匹配;
檔案使用前是否已經開啟;
是否處理了檔案尾;
是否處理了輸入/輸出錯誤;
輸出資訊中是否有文字性錯誤
(2) 模組局部資料結構測試;
檢查局部資料結構是為了保證臨時儲存在模組內的資料在程式執行過程中完整、正確。局部資料結構往往是錯誤的根源,應仔細設計測試案例,力求發現下面幾類錯誤:
不合適或不相容的類型說明;
變數無初值;
變數初始化或省缺值有錯;
不正確的變數名(拼錯或不正確地截斷);
出現上溢、下溢和地址異常。
除了局部資料結構外,如果可能,單元測試時還應該查清全域資料對模組的影響。
(3) 模組邊界條件測試;
邊界條件測試是單元測試中最後,也是最重要的一項任務。眾的周知,軟體經常在邊界上失效,採用邊界值分析技術,針對邊界值及其左、右設計測試案例,很有可能發現新的錯誤。
(4) 模組中所有獨立執行通路測試;
在模組中應對每一條獨立執行路徑進行測試,單元測試的基本任務是保證模組中每條語句至少執行一次。此時設計測試案例是為了發現因錯誤計算、不正確的比較和不適當的控制流程造成的錯誤。此時基本路徑測試和迴圈測試是最常用且最有效測試技術。計算中常見的錯誤包括:
誤解或用錯了算符優先順序;
混合類型運算;
變數初值錯;
精度不夠;
運算式符號錯。
比較判斷與控制流程常常緊密相關,測試案例還應致力於發現下列錯誤:
不同資料類型的對象之間進行比較;
錯誤地使用邏輯運算子或優先順序;
因電腦表示的局限性,期望理論上相等而實際上不相等的兩個量相等;
比較運算或變數出錯;
迴圈終止條件或不可能出現;
迭代發散時不能退出;
錯誤地修改了迴圈變數。
(5) 模組的各條錯誤處理通路測試。
一個好的設計應能預見各種出錯條件,並預設各種出錯處理通路,出錯處理通路同樣需要認真測試,測試應著重檢查下列問題:
輸出的出錯資訊難以理解;
記錄的錯誤與實際遇到的錯誤不相符;
在程式自訂的出錯處理段運行之前,系統已介入;
異常處理不當;
錯誤陳述中未能提供足夠的定位出錯資訊。
7. 單元測試用例及用例設計
瞭解了單元測試的任務,下面介紹測試案例,測試案例也是單元測試的核心,決定你的單元測試是否成功。測試案例的核心是輸入資料。預期輸出是依據輸入資料和程式功能來確定的,也就是說,對於某一程式,輸入資料確定了,預期輸出也就可以確定了,至於產生/銷毀被測試對象和運行測試的語句,是所有測試案例都大同小異的。
單元測試測試案例一般採用邏輯覆蓋法和基本路徑法進行設計。
(1). 邏輯覆蓋法
邏輯覆蓋是以程式內部的邏輯結構為基礎的測試案例設計技術,這一方法要求測試人員對程式的邏輯結構有清楚的瞭解。邏輯覆蓋可分為:語句覆蓋、判定覆蓋、條件覆蓋、判定-條件覆蓋、條件組合覆蓋與路徑覆蓋。
語句覆蓋就是設計若干個測試案例,運行所測程式,使得每一可執行語句至少執行一次。
判定覆蓋就是設計若干個測試案例,運行所測程式,使得程式中每個判斷的取真分支和取假分支至少經曆一次。
條件覆蓋就是設計若干個測試案例,運行所測程式,使得程式中每個判斷的每個條件的可能取值至少執行一次。
判定--條件覆蓋就是設計足夠的測試案例,使得判斷中每個條件的所有可能取值至少執行一次,同時每個判斷的所有可能判斷結果也至少執行一次。
條件組合覆蓋就是設計足夠的測試案例,運行所測程式,使得每個判斷的所有可能的條件取值組合至少執行一次。
路徑測試就是設計足夠的測試案例,覆蓋程式中所有可能的路徑。
(2) 基本路徑法
基本路徑測試法是在程式控制流程圖的基礎上,通過分析控制構造的環路複雜性,匯出基本可執行路徑集合,從而設計測試案例的方法。設計出的測試案例要保證在測試中程式的每個可執行語句至少執行一次。基本路徑測試法包括以下5個方面:
程式的控制流程圖:描述程式控制流程的一種圖示方法。
程式環境複雜性:McCabe複雜性度量;從程式的環路複雜性可匯出程式基本路徑集合中的獨立路徑條數,這是確定程式中每個可執行語句至少執行依次所必須的測試案例數目的上界。
匯出測試案例。
準備測試案例,確保基本路徑集中的每一條路徑的執行。
圖形矩陣:是在基本路徑測試中起輔助作用的軟體工具,利用它可以實現自動地確定一個基本路徑集。
(3)單元測試用例設計案例
好久前看過一篇非常不錯的單元測試用例設計案例,但沒有保留下來,感覺好可惜。一個好的單元測試案例對單元測試的入門是非常重要,希望這個案例能夠協助你對單元測試一些瞭解和對單元測試重要性的一個認識。
例如幣種換算單元函數,要對這個函數進行單元測試。應該怎麼去做個函數的單元測試呢?首先考慮的是該幣種換算單元函數的輸入輸出參數,其次是輸出結果與期望值是否相等;再次建立單元測試處理類,並相應建立測試該函數的測試方法,按照JUnit規範一般是test****()。
開始準備建立單元測試類及其測試方法,幣種換算函數的輸入參數為:目標幣種,轉換幣種,金額,輸出為轉換後目標幣種的金額。建立第一個簡單的單元測試處理類及單元測試測試方法:
Public BigDecimal testCurryChange(){
引用幣種換算函數並輸入測試參數,假如測試參數為:目標幣種為人民幣,轉換幣種為美元,金額為100.00。
BigDecimal bgResult = 799.95;
If(bdResult.equals(函數(”10”,”32”,100.00)== false){
Throw new Exception(“轉換錯誤!”);
}
}
第一個單元測試函數已經寫完畢,可以進行單元測試,但不能完全測試幣種換算函數是否完全正確,第一金額的不同轉換結構可能不同;第二不同幣種之間轉換,結果不同。那下一步應該怎麼做呢?
可以採用這樣的方法,建立同上的多個函數,但輸入參數不一樣,如金額不一樣;目標幣種與轉換幣種不一樣。這樣也可以在不同的方向來檢測幣種換算函數是否正確。一般情況下對於這種情況下,金額輸入參數處理會才用邊界發,取一個最小值和最大值作為輸入參數進行檢驗,另外是看實際情況,要換算多少中幣種,進行窮舉法,把所有的幣種進行處理,根據金額,幣種的組合分別進行測試。這兒就不一一寫出代碼。
但是上述方法雖然是一個可行的方法,但處理起來比較複雜,如果幣種換算比較多,組合起來要寫很多的單元測試方法,用起來比較煩瑣,那應該怎麼處理呢?在這種情況下可以採用數組的方法+迴圈的方法來實現,這樣就很容易避開上述方法的煩瑣。具體實現就不列舉出來,大家可以自行研究一下。
寫單元測試也是一個非常有趣的事情,上述方法在不同的情況都有可能實現,不是每一個單元測試案例都要追求最完美,能夠達到要求和實現目標即可。還有很多寫測試案例的方法就不一一列舉,大家可以根據實際情況而使用最好的方法。
8. 單元測試工具介紹
不同的開發語言,單元測試的工具也不一樣。JAVA語言開發系統:目前比較流行的是JUnit;.Net語言開發系統:目前比較流行的是:NUnit;C/C++語言開發系統:目前比較流行的是CPPUnit。目前系統開發是用的JAVA,於是對JUnit研究的比較多,有機會可以和大家共同探討一下,其他的如果有興趣,大家也可以自行研究。還有很多的單元測試工具,可以根據具體情況而選擇合適自己的單元測試工具。
JUnit的簡介:
http://blog.csdn.net/askmyself/archive/2005/10/10/498599.aspx
NUnit的簡介:
http://blog.csdn.net/litp/archive/2005/11/11/527684.aspx
http://blog.csdn.net/litp/archive/2005/11/11/527684.aspx
CPPUnit的簡介:
http://blog.csdn.net/lzw2005/archive/2005/09/23/488013.aspx
http://blog.csdn.net/casualgame/archive/2005/03/27/332162.aspx
通過單元測試的閱讀和瞭解,希望能夠把單元測試工具JUnit引入到目前的系統開發中來,而在實踐過程中發現並不是十分可行,在進行測試的參數中並不能很好地獲得一個資料庫連接。於是通過別的方法來代替JUnit而達到單元測試,自己編寫一個或多個單元測試業務處理類+Log4j(日誌記錄)也同樣能夠實現單元測試,相對來說這樣比較麻煩,要自己寫對象的建立釋放、測試業務處理等等。在實際情況中,或者能夠同樣遇到這樣的問題,希望大家能夠在實踐過程中找到自己合適的方法,而不是一定要採用單元測試工具。
9. 總結
總而言之,單元測試會讓我們的開發工作變得更加輕鬆,讓我們對自己的代碼更加自信。無論是大型項目還是小型項目,無論是時間緊迫的項目還是時間寬裕的項目,只要代碼不是一次寫完永不改動,編寫單元測試就一定超值,它已成為我們編碼不可缺少的一部分。
本文來自CSDN部落格,轉載請標明出處:http://blog.csdn.net/giftbook/archive/2005/12/26/562609.aspx