1、等價類分法的基本概念
等價類分法是將測試空間劃分成若干個子集,并且滿足每個子集中的任一數據對揭露程序中的缺陷都是等價的,這些子集就叫做等價類或者叫等價子集。
比如一個程序的輸入數據滿足 0<x<100為有效數據,其他為無效數據,那么就可以劃分成兩個等價類,一個是有效數據的等價類,另一個是無效數據的等價類,設計測試用例時就可以從這兩個等價類中分別取一個輸入數據來得到兩個測試用例。有效數據的等價類為1~99,所以可以從1~99中任意取一個數作為輸入數據來作為一個測試用例,從x不等于1~99中的數據中任意取一個數據作為輸入數據得到另一個測試用例。
1~99中的任一數據和其他數據都是等價的,比如使用了2來進行測試,那么可以假定數據2測試通過的話,1~99中的其他數據也能測試通過。
等價類分法可以用來對一些不能窮舉的集合進行合理分類,從各個等價類中選出有代表性的數據進行測試,從而保證設計出來的設計用例具有一定的代表性和一定范圍內的完整性,有效地縮減測試用例的數量。
等價類實際上是符合測試空間劃分原則的一種特殊劃分形式,即劃分完后的子集里的可測數據是等價的,而測試空間劃分原則則是要求里面有一個可測數據測試通 過能夠代表其他測試數據在滿足選取概率條件下也都可以通過。等價類選取測試數據時可以選取等價類中的任意數據作為測試數據,而測試空間劃分原則劃分的子集 一般是選擇指定的數據作為測試數據,如果按測試空間劃分原則劃分后的子集剛好成為了等價類才可以選擇里面的任一數據作為測試數據。
2、等價類的幾種類型
在現實情況中,由于缺陷的可能情況非常多,一個子集中的數據對某種缺陷是等價的,但對另外一種缺陷可能又是不等價的。所以把等價類分為弱等價類、強等價類、理想等價類三種類型。
1)弱等價類
弱等價類是考慮某個單一缺陷情況下的等價情況,子集里所有數據在這種缺陷假設下是等價的,并且劃分成的幾個等價類能夠覆蓋整個測試空間的單一缺陷。比如以下一段程序:
void Func(unsigned int x) { if ( x > 10 ) { Func1(); } else { Func2(); } } |
我們可以將數據劃分為兩個等價類,0~10為1個等價類,大于10的數據為1個等價類,在考慮“>”號誤寫成“<”號這種缺陷的情況下,這 兩個等價集中的數據都是等價的,比如0~10這個等價類中,使用0或使用10來進行測試都能發現缺陷。這兩個等價類中各自抽取一個測試數據進行測試,都能 代表其他數據揭示出“>”號誤寫成“<”號這種缺陷來,因此整個測試空間都被覆蓋了。
2)強等價類
強等價類是在多個缺陷假設前提下,各個等價類中的可測數據在單個或多個缺陷假設下是等價的,并且劃分的各個等價子集中各自取一個測試數據可以覆蓋整個測試空間的多個缺陷情況。
再考慮前面弱等價類中的例子程序,出錯的可能性有那些呢?除了大于號會錯寫成小于號外,實際上還有可能寫成大于等于號,10有可能寫成1或100等大于 10或小于10的數,為方便描述以錯寫成1和100為例,事實上錯誤成其他數和錯寫成1和100是等價的。這樣將各種可能出錯的情況組合起來,程序中的判 斷條件有可能有以下12種情況:
判斷條件 | 揭示缺陷的等價類 | 判斷條件 | 揭示缺陷的等價類 | 判斷條件 | 揭示缺陷的等價類 |
x>10 | | x>1 | {10} | x>100 | {11} |
x<10 | {>10} | x<1 | {>10} | x<100 | {0~9},{10} |
x<=10 | {10},{>10} | x<=1 | {>10} | x<=100 | {0~9},{10} |
x>=10 | {10} | x>=1 | {10} | x>=100 | {11} |
考慮0~10這個集合,在誤寫成中間一列條件中情況下,里面的數據并不等價,比如誤寫成x>1的情況下,使用1做測試和使用2做測試揭示缺陷是不同的,使用1做測試發現不了缺陷,但使用2測試就能發現缺陷。
在判斷條件誤寫成x>=10條件下,10和0~9中的任一數據也不等價,并且使用大于10的數據也無法揭示出條件錯寫成x>=10這個缺陷,因此整個測試空間的多個缺陷無法被已劃分的兩個等價類來覆蓋,10需要單獨劃分成一個等價類。
這樣將數據劃分成三個等價類{0~9}、{10}、{大于10的數據},再看看這三個等價類是否可以覆蓋表中各種出錯情況,顯然在x>100和 x>=100兩種情況下,大于10的數據集合中的數據是不等價的,使用大于100的數據不能揭示出缺陷,但使用大于10小于100的數據卻能揭示出 缺陷,因此需要對大于10的數據再劃分等價類,實際上只要將邊界值{11}劃一個單獨的等價類就可以了。
這 樣總共得到四個等價類{0~9}、{10}、{11}、{大于11的數據},從這四個等價類中各取一個數據的話就可以將以上列出的所有可能的缺陷情況都揭 示出來,但是各個等價類并不是對所有缺陷都等價的,這種劃分的等價類由于可以將各種缺陷情況覆蓋到,把它叫叫做強等價類。
3)理想等價類 這種等價類是嚴格按照等價類的定義來劃分的,即劃分的各個等價類中,每個等價類都滿足每個可測數據對揭示所有可能的缺陷都是等價的,并且劃分的各個等價類中各自任意取一個可測數據做為測試數據可以將全部的缺陷都揭示出來。
理想等價類在實際情況中是很罕見的,除非只有很少的一兩種可能的出錯情況,否則很難劃分成對揭示所有可能缺陷都等價的子集。所以在實際使用時,沒有必要去尋找理想等價類,否則徒然浪費時間,一般采用強等價類或弱等價類進行測試就足夠了。
3、等價類的判定方法
當將一個輸入域進行等價類劃分后,劃分出來的子集是否是等價的基本上靠經驗判斷,這給使用等價類分法帶來很大的難度,憑經驗劃分出來的等價類也許并不是真的等價類,如何才能確定劃分的類是等價類呢?
按照前面講過的弱等價類與強等價類的定義,要知道劃分的子集是否等價類先要知道又那些種類的可能缺陷,然后將劃分的等價類對照可能的缺陷進行驗證看是否能揭示出那些可能發生的缺陷。
這種判定方法的缺點是必須先知道會發生那些可能的缺陷,實際情況中往往并不知道所有可能的缺陷,那么在實際情況中如何采取一些簡單方法來判定一個子集是否是等價類呢?
當一個子集的處理過程與輸出完全一致時,基本上可以認為是等價類,處理過程是否相同很容易從需求和設計中得出。但是現實情況中往往同一個等價類中的不同數據對應的輸出結果并不相同,所以這種方法并不能對所有的情況都適用。
其實沒有什么特別好的辦法可以用來判斷一個子集中的任一數據對揭露程序中的缺陷都是等價的,除非將所有數據測試一遍。但是有一些條件可以協助判斷出某個子集不是等價類。
1)路徑判定法
最容易判定一個子集是否是等價類的方法就是路徑判定法,路徑判定法的基本思想是:對于子集中的任一數據,如果執行路徑并不完全相同,那么這個子集不是等價類。
需要注意的是,路徑判定法的反命題并不成立,即不能由執行路徑相同就推斷出子集中的數據是等價類。因為執行路徑相同情況下得到的結果不一定相同,舉例如下:
int mul(int a) { return (a*10000); } |
在mul()函數中,不論a輸入多少,執行路徑都只有一條,但是當a超過一定大小時,會出現整數乘法溢出,顯然不能將a的任意取值都作為等價類。
路徑相同之所以不能認為是等價類的根本原因在于程序設計中本身可能存在缺陷和遺漏,設計或編碼后的程序中的路徑本身就可能不正確,測試用例設計時不能假定程序中的路徑一定是正確的。
2)概率判定法
概率判定法是通過計算等價子集中的數據揭示某個缺陷的概率來進行判斷的方法。如果在一個等價子集中,所有數據的揭示缺陷的概率不相同并有一定差距,那么可以認為不是等價類。
這個判定法的使用并不是說事先需要知道所有可能發生的缺陷,它只需要找到一個缺陷來證明在這種缺陷情況下,子集中的數據在揭示這個缺陷方面的概率是不相同的,那么就可以認為在這種缺陷條件下不是等價類,至少可以認為不是強等價類。
比如前面講的x>10的劃分,{0~10}這個集合中,在寫成x>=10的情況下,10和子集中其他數據揭示缺陷的概率是不同 的,0~9都不能發現缺陷,測試會通過,也就是說揭示出這個缺陷的概率為0,而10則能揭示出這個缺陷,所以它們不能劃分到同一個等價類里面。