單元測試中測試用例的設計方法
1. 用於語句覆蓋的基路徑法
基路徑法保證設計出的測試
用例,使程式的每乙個可執行語句至少執行一次,即實現語句覆蓋。基路徑法是理論與應用脫節的典型,基本上沒有應用價值,讀者稍作了解即可,不必理解和掌握。
基路徑法步驟如下:
1)畫出程式的控制流圖
控制流圖是描述程式控制流的一種圖示方法,主要由結點和邊構成,邊代表控制流的方向,節點代表控制流的匯聚處,邊和結點圈定的空間叫做區域,下面是控制流圖的基本元素:
以下**:
void sort(int irecordnum, int itype)
elseif(1 == itype)
else}}
可以畫出以下控制流圖:
2)計算程式環路複雜度
環路複雜度v(g)可用以下3種方法求得:
(1) 環路複雜度等於控制流圖中的區域數;
上圖中,有4個區域,v(g) = 4。
(2) 設e為控制流圖的邊數,n為結點數,則環路複雜度為e-n+2;
上圖中,v(g) = 10(邊) – 8(結點) + 2 = 4。
(3) 設p為控制流圖中的判定結點數,環路複雜度為p+1。
上圖中:v(g) = 3(判定結點) + 1 = 4。
環路複雜度是獨立路徑數的上界,也就是需要的測試用例數的上界。
3)匯出基本路徑集
基本路徑數等於v(g)。根據上面的計算方法,可得出需要的基本路徑數為4。路徑就是從程式的入口到出口的可能路線,基本路徑要求每條路徑至少包含一條新的邊,直到所有的邊都被包含。需要提醒的是:基路徑法和路徑覆蓋是兩回事,用於設計用例的基路徑數一般小於全部路徑數,即基本路徑集不是惟一的。基路徑法完成的是語句覆蓋,而不是路徑覆蓋。下面選擇四條基本路徑:
路徑1:1-11
路徑2:1-2-3-4-5-1-11
路徑3:1-2-3-6-8-9-10-1-11
路徑4:1-2-3-6-7-9-10-1-11
4) 設計用例
根據上面的路徑,可以設計出以下用例:
路徑1:1-11
用例1:irecordnum = 0
路徑2:1-2-3-4-5-1-11
用例2:irecordnum=1, itype = 0
路徑3:1-2-3-6-8-9-10-1-11
用例3:irecordnum=1, itype = 1
路徑4:1-2-3-6-7-9-10-1-11
用例4:irecordnum=1, itype = 2
從上述步驟可以看出,基路徑法工作量巨大,如果用於五十行左右的函式,將耗費大量的時間,而五十行**的函式實在是太普通了。這種成本巨高的方法,其測試效果如何呢?測試效果完全與成本不匹配,首先,基路徑法完成的只是**覆蓋,這是最低級別的覆蓋,其次,整個設計過程都是依據已經存在的**來進行的,沒有考慮程式的設計功能,是典型的「跟著**走」,不足是顯而易見的。綜上所述,基路徑法沒有實際應用價值。
2. 用於mc/dc的真值表法
設計用於mc/dc的用例,可以先將條件值的所有可能組合列出**,然後從中選擇用例,稱為真值表法。例如判定a || (b && c),條件組合如下表:
為了使a獨立影響判定結果,選擇b和c相同,判定結果相反,且a相反的組合:組合2和6;
為了使b獨立影響判定結果,選擇a和c相同,判定結果相反,且b相反的組合:組合5和7;
為了使c獨立影響判定結果,選擇a和b相同,判定結果相反,且c相反的組合:組合5和6。
因此,組合2、5、6、7符合mc/dc要求。符合mc/dc要求的用例集不是惟一的。
為了提高效率,可以使用工具來生成真值表和找出符合要求的組合,有些商業工具具有這種功能。自行開發難度也不大,下面提出開發
mc/dc用例設計小工具的思路,有興趣的讀者可以嘗試一下:
1)用乙個簡單的詞法和語法分析器解析判定表示式,計算條件數量;
2)生成真值表;
3)用乙個邏輯表示式計算器,針對每個條件c,掃瞄真值表,找出符合以下要求的組合:除條件c外,其他條件取值相同;將條件c的真值和假值分別代入判定表示式,判定的計算結果相反。
4)針對找出的組合,設計兩個用例,條件c分別取真和假。
需要注意的是,判定中可能存在完全相同的條件,例如:
(a==0 || b == 1) && c == 2 || (a==0 && d == 3)
針對a==0設計mc/dc用例時,前乙個a==0取反,後乙個a==0也會跟著取反,如果後乙個a==0視為其他條件,則不能實現mc/dc覆蓋,因此,計算判定值時,兩個a==0應視為同乙個條件。
3 邊界值法
邊界值法假定錯誤最有可能出現在區間之間的邊界,一般對邊界值本身,及邊界值的兩邊都需設計測試用例。
如下函式:
//引數age表示年齡
int func(int age)
引數age表示乙個人的年齡,假設有效的取值範圍是0-200,那麼,用邊界值法可以得出以下用例(省略輸出):
用例1:age = -1;
用例2:age = 0;
用例3:age = 1;
用例4:age = 199;
用例5:age = 200;
用例6:age = 201;
通常,程式對輸入還會分段處理,例如,年齡在10以下,為兒童,需要特別照顧;年齡在60歲以上,為退休老人,不能安排工作,那麼,10和60是內部邊界,也要設計測試用例:
用例7:age =9;
用例8:age = 10;
用例9:age = 11;
用例10:age = 59;
用例11:age = 60;
用例12:age = 61;
邊界值法需要了解資料所代表的實際意義,此外對於列舉型別等非標量資料不適用。邊界值法對於複雜的軟體專案來說,適用範圍有限。
4 等價類法
先從**編寫的思路說起。程式設計師編寫乙個函式的**,會如何做呢?
首先,了解**功能。程式的功能是什麼?無非就是:有哪些輸入?執行什麼操作或計算?產生什麼輸出?
然後,將功能細化,形成乙個或多個功能點。乙個功能點就是一類輸入及其處理。什麼叫「一類」輸入?程式可能有無數輸入,但**並不需要用無數個判定來對每個輸入分別做處理,只需將輸入分類,需要做相同處理的輸入歸於一類,這就是「等價類」。從程式設計角度來說,「等價類」是指計算或操作過程的「等價」,乙個等價類就是處理過程完全相同的輸入的集合。程式中通常用判定來識別分類,乙個判定就是一次分類,巢狀的判定則會造成分類數量的翻番。
所以,函式**編寫的核心思維就是等價類劃分和處理。乙個函式要完全正確,關鍵是等價類的劃分要正確完整,且每個等價類的處理正確。
舉個例子,現在要編寫乙個函式,將字串左邊的空格刪除。函式原形如下:
char* strtrml(char *str);
功能:將str左邊空格刪除,並返回str本身。
功能點:
1. 左邊有空格:刪除;(正常輸入)
2. 左邊無空格:不作處理;(正常輸入)
3. 全部是空格:全部刪除;(正常輸入)
4. 空串:不作處理;(邊界輸入)
5. 空指標:直接返回。(非法輸入)
不一定需要針對每個功能點分別寫**,因為程式中的if、for、while等語句本身具有「如果不符合條件就跳過」的含義,所以很多功能點是可以共用**的,例如,前4個功能點只需要相同的**,不過,程式設計時對功能點的考慮還是要全面。
既然函式沒有錯誤的關鍵是等價類劃分正確完整且處理正確,那麼測試時,只要把輸入的等價類都列出來,並設定正確的預期輸出,進行測試就行了。
這就是通常說的「等價類」法,從測試角度來說的「等價」,是指測試效果上的等價,即同類中乙個資料測試通過,可以肯定其他資料也會測試通過。
用例設計的首要工作是設定輸入。輸入有哪些呢?要從正常輸入、邊界輸入、非法輸入三方面考慮,每方面進一步劃分形成等價類,即要考慮:有哪些正常輸入?有哪些邊界輸入?有哪些非法輸入?
多個輸入時,例如有多個引數,首先把各個引數的等價類列出來,然後要考慮引數之間是否存在特殊的組合關係。例如下面的函式:
//計算某年某月某日是星期幾,引數分別表示年月日
int date(int year, int month, int day);
用例如下表(假設year的有效範圍是1-9999):
用例的輸出是比較容易被輕視的工作,但是,沒有充分且正確的預期輸出,用例基本上沒有意義,就像醫生要求病人做一大堆檢查,卻不看檢查結果一樣。預期輸出要根據程式的設計功能確定正確的值。乙個用例的預期輸出可能有多個。
等價類法是與程式的基本特性「對資料分類處理」相匹配的方法。對於乙個函式來說,如果對資料的分類正確且完整,每乙個分類處理正確,那麼,程式就沒有問題。同樣,測試時,只要依據設計功能,找出所有等價類,那麼,用例就是完整的。所以,用例的完整性,本質上是指等價類是否劃分正確且完整,每一類的正確輸出是否均依據設計功能正確設定。
使用了等價類法後,是否需要使用其他方法呢?
等價類法從「有哪些正常輸入?有哪些邊界輸入?有哪些非法輸入?」三個方面來考慮等價類,因此,邊界值法是等價類法的一部分。
常見的用例設計方法中還有正交法和錯誤推測法。正交法考慮資料的組合,實際上,如果程式對輸入資料的組合需要判斷處理,也是一種等價類劃分,但正交法會產生大量的多餘組合,且可能缺少必要的組合,因此不推薦採用正交法,應該根據資料的實際意義自行組合。單獨從錯誤推測角度去設計用例未免太不可靠,但錯誤推測法可以作為檢查等價類是否完整的一種思路,即用等價類法設計用例後,可以考慮哪些輸入比較容易產生錯誤,以檢查是否遺漏,這只是一種檢查思路,也包含在等價類法之中。總之,用例設計只需使用等價類法,但可以從多種角度檢查等價類的完整性。
python單元測試用例設計
在剛學習程式設計的時候總是忘記處理一些特例 尤其是動態語言可以傳各種值 為了養成良好的程式設計和測試習慣,在編寫單元測試用例的時候,我們注意考慮下如下測試用例 等價類劃分 def binary search array,target if not array return 1 beg,end 0,l...
單元測試,測試用例 GO語言
資料夾目錄 split string split.go split string split test.go 執行測試函式 package split string import reflect testing 單元測試 可以和測試 放到一起,用同樣的包名 以測試 名開頭,以 test.go 結尾哦...
例解 整合測試用例與單元測試用例的區別
函式一 getmaxintwo int a,int b 函式二 getmaxinthree int a,int b,int c 單元測試用例的設計 getmaxintwo的ut 用例 3 2 1,3 2 2 語句覆蓋率為 100 getmaxinthree的ut 用例 1 2,3 語句覆蓋率為 10...