單元測試徹底測試的方法
單元測試(模塊測試)是開發者編寫的一小段代碼,用于檢驗被測代碼的一個很小的、很明確的功能是否正確。通常而言,一個單元測試是用于判斷某個特定條件(或者場景)下某個特定函數的行為。下面我們一起來看,徹底測試的方法。
1、徹底測試的可能性
有一種說法是,“測試總是不徹底的”,對于軟件整體來說,要做到徹底測試確實很難,但對于局部代碼,則是完全可能的。先說 “理想的徹底測試”,對于一段程序,比如一個函數,如果它的所有可能輸入都測試過,并且都沒有錯誤,那么可以肯定,這個函數是沒有錯誤的,這就是 “理想的徹底測試”。“理想的徹底測試”是做不到的,有沒有 “現實的徹底測試”?有!“現實的徹底測試”就是所有的等價類都經過測試。
先說說等價類。“類”是指把輸入數據分組,“等價”是指測試效果上的等價,“等價類”就是在一組輸入數據中,任取一個進行測試,如果測試通過,那么,可以認為同組的其他輸入數據也可以測試通過,也就是說,測試了一個,就等于測試了一組。如果把所有的等價類都找出來進行測試,就相當于所有輸入都經過測試,這就是“現實的徹底測試”。
對于一段程序來說,“等價類”通常是有限的,因此,從理論上來說,對局部代碼進行“現實的徹底測試”是完全可能的。
2、等價類劃分的基本方法
通常從三個方面考慮程序的輸入:正常輸入,邊界輸入,非法輸入。
正常輸入:例如字符串的Trim函數,功能是將字符串前后的空格去除,那么正常的輸入可以有四類:前面有空格;后面有空格;前后均有空格;前后均無空格。
邊界輸入:上例中空字符串可以看作是邊界輸入,如果輸入是一個指針的話,空指針也算是邊界輸入。再如一個表示年齡的參數,它的有效范圍是0-100,那么邊界輸入有兩個:0和100。
非法輸入:非法輸入是正常取值范圍以外的數據,或使代碼不能完成正常功能的輸入,如上例中表示年齡的參數,小于0或大于100都是非法輸入,再如一個進行文件操作的函數,非法輸入可能有:文件不存在;目錄不存在;文件正在被其他程序打開;權限錯誤。
一般情況下,只要考慮:有哪些正常輸入?有哪些邊界輸入?有哪些非法輸入?就可以找出大多數等價類,例如,一個函數,功能是把小寫金額轉換成大寫,輸入是小寫數字,輸出是大寫金額,那么:
正常輸入有:只有整數,只有小數,既有整數又有小數
邊界輸入有:整數部分很大,0.0,小數位數超過兩位
非法輸入有:空串,非數字,負數
“等價類”這個詞是從測試的角度來說的,從開發的角度來看,“等價類”與“功能點”具有對應關系,例如,字符串的Trim函數,功能點有:
如果只有左邊有空格,返回刪除左邊空格后的結果(等價類:左邊有空格);
如果只有右邊有空格,返回刪除右邊空格后的結果(等價類:右邊有空格);
如果兩邊都有空格,返回刪除兩邊空格后的結果(等價類:兩邊有空格);
如果兩邊都沒有空格,返回原串(等價類:兩邊無空格);
如果是空串,直接返回(等價類:空串);
如果是空指針,直接返回(等價類:空指針);
……
一個“等價類”對應程序的一個“功能點”,如果程序的所有功能點都正確實現了,那么這個程序的功能就肯定沒問題。程序員在寫代碼時肯定要想清楚程序的主要功能點,否則代碼無從寫起,因此,如果程序員邊編碼邊測試的話,大部分等價類都是現成的。如果由測試部門做單元測試,測試部門只能依據設計文檔來測試,設計文檔也會規定程序的功能,要不然就無從測起,同樣可以說大部分等價類是現成的。
3、徹底測試的基本思路
前面說過:如果把所有的等價類都找出來進行測試,就相當于所有輸入都經過測試,這就是“現實的徹底測試”。
如何把所有的等價類都找出來?根據程序的功能,大部分等價類都是容易想到的,甚至是現成的,但是,如何衡量是否完整?例如,前面所舉的兩個例子:刪除字符串兩邊空格的函數和將金額小寫轉大寫的程序,我們可以肯定,等價類最多也就十幾個,相當有限,但是,已經列出的等價類是完整的嗎?有沒有遺漏的?
要找出所有等價類,關鍵是要有衡量完整性的指標。等價類實際上就是程序的功能點,程序的功能是人為規定的,很難衡量完整性,但白盒測試卻很容易衡量完整性,因此,我們可以考慮使用白盒測試的覆蓋率來衡量測試完整性。
看一下這個簡單的例子:
- void Func(int* p)
- {
- if(p)
- {
- *p = 0;
- }
- else
- {
- return;
- }
- }
參數p是一個指針,測試時當然要將空指針作為一個等價類,如果漏了這個等價類,會怎么樣呢?分支覆蓋會不完整:else分支未覆蓋。從這個例子可以看出,未覆蓋的邏輯單位通常對應未測試的等價類,因此,白盒覆蓋可以衡量等價類是否完整。
還是上面的例子,假如程序員完全忘了有空指針這回事,把代碼寫成這樣:
- void Func(int* p)
- {
- *p = 0;
- }
由于判斷p是否為空指針的代碼不存在,白盒覆蓋當然不會提示說“某某代碼或某某分支未覆蓋”,因此,白盒覆蓋不能發現“程序員未處理某些特殊輸入”這種情形,也就是說,即便達到了***的白盒覆蓋率,仍然不能保證找出所有等價類。
程序員會忘記處理哪些輸入呢?常見的輸入一般是不會記的,否則程序的起碼功能都未實現,容易忘記的是一些“偏僻”的輸入,例如,空指針、空字符串、很大的數、很小的數、合法取值邊界附近的值等等,從輸入的角度來看,這些特殊值通常跟數據類型有關,從程序的行為來看,這些特殊輸入常常會導致崩潰、產生異常,或超時,即具有行為特征,正好是自動動態測試可以發現的,因此,我們可以利用自動動態測試來捕捉“程序員未處理某些特殊輸入”形成的錯誤。
4、徹底測試方法
基于上一節的思路,經過長時間摸索和改進,我們提出了實現徹底測試的“三步法”:
1)根據代碼的功能,人工設計測試用例進行基本功能測試;
2)統計白盒覆蓋率,為未覆蓋的白盒單位設計測試用例,實現完整的白盒覆蓋,比較理想的覆蓋率是實現100%語句、條件、分支、路徑覆蓋;
3)自動生成大量的測試用例,捕捉“程序員未處理某些特殊輸入”形成的錯誤。
第1步的測試用例通常是現成的,因為詳細設計文檔會規定程序的基本功能,沒有文檔的,程序員在編程時也要想清楚程序的功能,這些基本功能就是基本測試用例;
第2步是在第1步的基礎上,檢查未覆蓋的白盒單位,由于未覆蓋的邏輯單位通常對應未測試的等價類,因此第2步可以找出第1步所遺漏的測試用例;
第3步用自動動態測試彌補第2步的固有缺陷。
“三步法”盡量避免重復工作,白盒方法和黑盒方法相結合,人工方法和自動方法相補充,如果第2步的覆蓋率比較理想,那么基本上可以保證找出所有等價類。在開發過程允許的限度內,“三步法”已接近極限,當得起“徹底測試”四個字。
5、是否需要其他測試?
“三步法”使用了人工動態測試和自動動態測試,未使用人工靜態分析和自動靜態分析,要不要再使用這兩種方法呢?這就要看合不合算了,畢竟項目時間和預算是有限的。
“三步法”的測試完整性是***的,讀者可以比較一下,自己參與過的項目,所要求達到的覆蓋率是怎么樣的?做得比較好的,一般只是要求達到100%的代碼覆蓋,執行中還未必能做到,而“三步法”所要達到完整性是這樣的:1)100%語句、條件、分支、路徑覆蓋;2)用自動動態測試捕捉未考慮某些特殊輸入形成的錯誤。經過這種徹底測試后,遺留的錯誤可以說已經極少了。
自動靜態分析只能發現語法特征錯誤,現代編譯器對多數這類錯誤會給出警告,另外,現有的自動靜態分析技術會產生大量的誤報,在一大堆報告中去一條一條人工辨別,尋找遺漏的錯誤,無異于“大海描針”。人工靜態分析也差不多。先做這兩項又怎么樣?顯然也是沒必要的。
為了統一代碼風格,可以偶爾抽查代碼,也就是說,可以用人工靜態方法檢查代碼風格和編碼規范。
原文鏈接:http://www.51testing.com/html/78/n-222978.html
【編輯推薦】