測試覆蓋率


什么是測試覆蓋率

首先,該如何評審自己寫的測試用例是否滿足測試要求?是否存在漏洞與缺陷?

這就要引入一個測試覆蓋率的概念了。

測試覆蓋率

覆蓋率是用來度量測試完整性的手段,是測試效果衡量的標准,是測試技術有效性的度量:

覆蓋率 = (至少被執行一次的項目(item)數) / (項目的總數)

項目是指:語句、判定、分支、函數等等。

覆蓋率按照測試方法一般可分為三大類:

  • 白盒覆蓋率:語句、判定、條件、路徑等等;
  • 灰盒覆蓋率:接口相關;
  • 黑盒覆蓋率:功能、性能測試;

另外還包括面向對象覆蓋率等。

注意,測試用例設計不能一味的追求覆蓋率,因為測試成本隨覆蓋率的增加而增加,要在覆蓋率和成本之間有所取舍。

白盒覆蓋率

白盒覆蓋率中使用的最常見的就是邏輯覆蓋率(Logical Coverage),也稱為代碼覆蓋率(Code Coverage)、或者稱為結構化覆蓋率(Structural Coverage)。

邏輯覆蓋率包括:

  • 語句覆蓋;
  • 判定覆蓋;
  • 條件覆蓋;
  • 判定條件覆蓋;
  • 條件組合覆蓋;
  • 路徑覆蓋;

語句覆蓋

語句覆蓋(Statement Coverage)的含義是,在測試時運行被測程序后,程序中被執行到的可執行語句的比率:

語句覆蓋率 = (至少被執行一次的語句數量) / (可執行的語句總數)

現在我們祭出在測試覆蓋率篇中都會使用的一張圖。

這個一個函數的流程圖,需要傳入三個參數;有兩個語句,每個語句中有兩個判斷條件;根據傳入參數的不同,它有幾種可能的執行過程:abdabe等等。牢記這張圖。

我們用Python代碼實現這個函數:

def foo(a, b, x): if a > 1 and b == 0: x /= a if a == 2 or x > 1: x = x + 1 case1 = foo(a=2, b=0, x=3) case2 = foo(a=2, b=1, x=3)

上面的示例中,我們分別寫了兩個用例case1case2

那么它們的用例怎么設計呢?又怎么計算語句覆蓋率呢?

在測試時,首先設計若干個測試用例,然后運行被測程序,使程序中的每個可執行語句至少執行一次。

首先來看case1,在函數foo中,因為每個語句都包含兩個子條件,所有共有4個語句,當然,你也可以只根據大的語句來划分,那就是2個語句。

根據case1的傳入參數a=2, b=0, x=3,它符合:

編號 條件取值 標記
1 A>1 T
2 B==0 T
3 A==2 T
4 X>3 F

在程序運行中,上面4種情況都會覆蓋到,所以,根據上面的公式:

34=75%34=75%


被執行的語句是4條除以可執行的語句總數4條,它的覆蓋率是100%。

 

case2,根據傳入參數a=2, b=1, x=3

編號 條件取值 標記
1 A>1, B==0 T,F
2 A==2, X>1 T,F

因為and的關系,上面第一條語句不會執行。所以,它的覆蓋率50%。

12=50%12=50%


需要注意的是,有時候語句覆蓋率達到100%也會有缺陷發現不了,比如開發人員將第二個語句寫成了and

 

def foo(a, b, x): if a > 1 and b == 0: x /= a if a == 2 and x > 1: # 偷偷將 or 替換為 and x = x + 1 case1 = foo(a=2, b=0, x=3) case2 = foo(a=2, b=1, x=3)

自己算覆蓋率吧!

所以覆蓋率只是我們度量的一個手段。

判定覆蓋率

判定覆蓋(Decision Coverage)也叫分支覆蓋(Branch Coverage),它是指在測試時運行被測試程序后,程序中所有判定語句的取真分支和取假被執行到的比率:

判定覆蓋率 = (判定結果被評價的次數) / (判定結果的總數)

一般的判定條件可能有這些:ifif\elseif\elif\else等等。

這個怎么玩呢?把最開始的程序圖拿過來(你想象它在這!)。

def foo(a, b, x): if a > 1 and b == 0: x /= a if a == 2 or x > 1: x = x + 1 case1 = foo(a=2, b=0, x=3) case2 = foo(a=1, b=0, x=1)

我們設計了兩個用例,來判斷取真取假的情況。

來看case1,參數是a=2, b=0, x=3

編號 條件取值 標記
1 A>1, B==0 T
2 A==2, X>1 T

在來看case2,參數是a=1, b=0, x=1

編號 條件取值 標記
1 A>1, B==0 F
2 A==2, X>1 F

case1走了兩次真,case2走了兩次假,我們認為這兩個用例的判定覆蓋率達到了100%:

44=100%44=100%


需要注意的是,有時候語句覆蓋率達到100%也會有缺陷發現不了,比如開發人員將第二個條件語句的X>1寫成了X<1它的執行情況是:

 

用例 編號 條件取值 標記
case1 1 A>1, B==0 T
case1 2 A==2, X>1 T
case2 3 A>1, B==0 F
case2 4 A==2, X<1 F

此時的判定覆蓋率仍然為100%,但是程序已經存在問題了。

條件覆蓋率

條件覆蓋(Condition Coverage)的含義時,在測試時運行被測程序后,所有的判斷語句中每個條件的可能取值(真和假)出現過的比率:

條件覆蓋率 = (條件操作數值至少被評價一次的數量) / (條件操作數的總數)

def foo(a, b, x): if a > 1 and b == 0: x /= a if a == 2 or x > 1: x = x + 1 case1 = foo(a=2, b=0, x=3) case2 = foo(a=1, b=0, x=1) case3 = foo(a=2, b=1, x=1)

這個又該怎么玩呢?

我們在設計用例時,要使每個判斷中的每個條件的可能值都要滿足一次,這么說,上面的程序有4個語句,每個語句都有兩種情況,所以,共有八種情況:

條件 取真 取假 標記
A>1 T F T,F
B==0 T F T,F
A==2 T F T,F
X>1 T F T,F

設計測試用例(看着那個圖!):

測試用例 參數:A B X 執行路徑 覆蓋條件
case1 203 ace T,T,T,T
case2 101 abd F,T,F,F
case3 211 abe T,F,T,F

case1執行了四個真,case2執行了三個假,漏掉一個假,所以,我們又設計了case3來補上這個假,最終它們的條件覆蓋率是:

88=100%88=100%


八種情況都覆蓋了。
沒完!覆蓋了條件的測試用例不一定覆蓋了分支:

 

測試用例 參數:A B X 執行路徑 覆蓋條件
case1 103 abe F,T,F,T
case2 211 abe T,FT,F

八種條件都走完了,但是用例少覆蓋了分支acd

判定條件覆蓋率

判定條件覆蓋率(Decision Condition Coverage)也叫分支條件覆蓋率(Branch Condition Coverage)。所謂判定條件覆蓋就是設計足夠的測試用例,使得判斷中每個條件的所有可能取值至少執行一次,同時每個判斷本身的所有可能判斷結果至少執行一次:

判定條件覆蓋率 = (條件操作數值或判定結果至少被評價一次的數量) / (條件操作數值 + 判定結果總數)

def foo(a, b, x): if a > 1 and b == 0: x /= a if a == 2 or x > 1: x = x + 1 case1 = foo(a=2, b=1, x=3) case2 = foo(a=0, b=0, x=1)

我們先單獨算出來case1的條件覆蓋率和判定覆蓋率。

先來看條件的:

條件 取真 取假 標記
A>1 T F T,F
B==0 T F T,F
A==2 T F T,F
X>1 T F T,F

因為是8個條件。所以結果是4/8,覆蓋率也是50%。

再來看判定,參數是a=2, b=1, x=3

編號 條件取值 標記
1 A>1, B==0 T,F
2 A==2,X>1 T,T

雖然4個條件得的結果是TFTT,但由於第一個條件是and,而結果是TF,所以,不執行,所以結果是2/4,覆蓋率是50%。

那么判定條件覆蓋率怎么算呢?

48+24=61248+24=612


在來個示例,看看case2的,參數是a=0, b=0, x=1

 

先來看條件的:

編號 條件取值 標記
1 A>1, B==0 F,T
2 A==2,X>1 F,F

因為是8個條件(四個語句分別取真假)。所以結果是4/8,覆蓋率也是50%。

再來看判定,參數是a=0, b=0, x=1

條件 取真 取假 標記
A>1,B==0 T F F,T
A==2,X>1 T F F,F

4個語句得的結果是FTFF,所以結果是2/4,覆蓋率是50%。

最后的判定條件覆蓋率仍然是6/12

48+24=61248+24=612

 

條件組合覆蓋率

再說條件覆蓋率之前先來看個示例:

def foo(a, b, x): if a > 1 and b == 0: x /= a if a == 2 or x > 1: x = x + 1 case1 = foo(a=2, b=0, x=3) case2 = foo(a=1, b=1, x=1)

上例中,兩個用例的判定:

用例名 取值 標記
case1:203 A>1,B==0 T,T
case1:203 A==2,X>1 T,T
case2:111 A>1,B==0 F,T
case2:111 A==2,X>1 F,F

匯總一下,兩個用例的判定都覆蓋到了,所以是4/4

而條件覆蓋率:

用例名 取值 標記
case1:203 A>1,B==0 T,T
case1:203 A==2,X>1 T,T
case2:111 A>1,B==0 F,F
case2:111 A==2,X>1 F,F

8個條件都覆蓋到了,所條件覆蓋率是8/8,那么,它們的判定條件覆蓋率是12/12

44+88=121244+88=1212


也就是說,現在判定條件覆蓋率是100%。

 

現在,開發把上面兩個條件的and 和or條件互換了。那么用上面的兩個case它們的判定條件覆蓋率同樣是100%。

def foo(a, b, x): if a > 1 or b == 0: x /= a if a == 2 and x > 1: x = x + 1 case1 = foo(a=2, b=0, x=3) case2 = foo(a=1, b=1, x=1)

判定:

用例 取值 標記
case1:203 A>1,B==0 T,T
case1:203 A==2,X>1 T,T
case1:111 A>1,B==0 F,F
case2:111 A==2,X>1 F,F

兩個用例的判定仍然是4/4。條件是8/8,那么判定條件覆蓋率是12/12,最終的結果是100%。所以開發寫錯了代碼,這兩個用例仍然沒有發現這類問題。

針對這類問題,就要使用條件組合覆蓋率了。

條件組合覆蓋(Multiple Condition Coverage)的基本思想是設計足夠的測試用例,使每個判定中條件的各種可能的組合都至少出現一次:

條件組合覆蓋率 = (條件組合至少被評價一次的數量) / (條件組合總數)

同樣是上面的示例。

每個判斷中所有的組合都要覆蓋到。

組合編號 條件取值 標記
1 A>1,B==0 T,T
2 A>1,B!=0 T,F
3 A<=1,B==0 F,T
4 A<=1,B!=0 F,F
5 A==2,X>1 T,F
6 A==2,X<=1 T,F
7 A!=2,X>1 F,T
8 A!=2,X<=1 F,F

兩個判定,每個判定中有兩個條件,所有共有八種條件,所以,要想達到條件組合覆蓋率100%的話,這八種情況都要覆蓋到。

測試用例 參數:A B X 覆蓋組合編號 所走路徑 覆蓋條件
case1 203 1,5 ace TTTT
case2 211 2,6 abe TFTF
case3 103 3,7 abe FTFT
case4 111 4,8 abd FFFF

PS:覆蓋組合編號內容是上上表中的組合編號。

上例中的四個測試用例覆蓋了所有的條件、分支。

所以,條件組合覆蓋率是100%的話,那么,條件、判定、語句都會達到100%。

但從程序運行路徑角度來看,僅覆蓋了3條路徑,遺漏掉了acd。

所以,目前為止,並沒有一種覆蓋率能保證測試覆蓋率達到100%。

路徑覆蓋率

路徑覆蓋(Path Coverage)是指在測試時運行被測程序后, 程序中所有可能的路徑被執行過的比率。

路徑覆蓋率 = (至少被執行到一次的路徑數) / (總的路徑數)

還是那個示例,還是那個圖!

如果你看着圖的話, 程序執行的路徑有以下幾條:

測試用例 參數:A B X 覆蓋路徑
case1 203 ace
case2 101 abd
case3 211 abe
case4 301 acd

所以,我們設計的測試用以,要把這些路徑都要覆蓋到。

看着采用路徑覆蓋率好使,但是仍然不是完美的。

如果開發將程序寫錯成了:

def foo(a, b, x): if a > 1 and b == 0: x /= a # if a == 2 or x > 1: # 正確的 if a == 2 and x >= 1: # 開發寫錯后的 x = x + 1

那么,在這個錯誤示例中,用上面的幾個用例,仍然每個路徑都能覆蓋到,但是這個錯誤卻不能發掘出來。

所以說,每種覆蓋率都有自己的局限性,因此在測試中, 要把各種覆蓋率組合起來對測試用例進行綜合考量。

一般的,我們選擇覆蓋率優先級是:

  • 語句;
  • 判定;
  • 條件;
  • 條件組合;
  • 路徑;

在白盒這里的幾種覆蓋率,大致就是這些,我們來看其他的。

灰盒覆蓋率

灰盒覆蓋率這主要包括:

  • 函數覆蓋;
  • 接口覆蓋;

函數覆蓋

函數覆蓋(Function Coverage)是針對系統或者一個子系統測試,它表示在該測試中,有那些函數被測試到了。其被測到的頻率有多大,這些函數在系統所有函數中的比例有多大,函數覆蓋是一個比較容易自動化的技術:

函數覆蓋 = (至少被執行一次的函數數量) / (系統中函數的總數)

假如有100個函數,在測試過程中,有90個函數(至少被執行了一次的函數)被測試到了, 所以它的覆蓋率是90%。

也有很多測試工具提供了了函數覆蓋,比如說TrueCoveragePureCoverage等。

接口覆蓋率

接口覆蓋(Interface Coverage)也叫做入口點覆蓋(Entry-Point Coverage),要求通過設計一定的用例使系統的每個接口被測試到:

接口覆蓋率 = (至少被執行一次的接口數量) / (系統中接口的總數)

被測試的接口可能是對外提供訪問的接口,也可能是內部接口。

接口可是是函數與函數之間,模塊與模塊之間,子系統與子系統之間的接口。

我們主要關注接口之間的數據交換、有沒有邏輯依賴等等。

比如有兩條測試用例去測試4個接口,那么覆蓋到了其中的兩個,那么它的接口覆蓋率是2/4

黑盒覆蓋率

黑盒測試主要應用在系統測試階段,那么在系統測試階段,它的測試過程一般是這樣的。

  • 根據需求提取需求項;
  • 根據需求項提取功能點;
  • 根據功能點設計測試用例。

所以,在黑盒覆蓋率這塊,主要就是做功能覆蓋率(Functional Coverage)。

而在功能覆蓋率中最常見的是需求覆蓋(Requirement Coverage),其含義是通過設計一定的測試用例,要求每個需求點都被測試到。

需求覆蓋率 = (被驗證到的需求數量) / (總的需求數量)

功能覆蓋方面的自動化工具比較少。

當然,以上是針對系統測試階段來划分的。

當然,單元測試階段,也能進行黑盒測試。

比如,我們對一個函數進行測試。我們可以把函數當成一個黑匣子,我們關注入參和返回的兩邊數據即可,而不用特意關注函數內部的實現。

面向對象覆蓋率

結構化覆蓋率用來度量測試的完整性已經被大家所接受,但是這個技術在面向對象領域卻遇到挑戰,由於傳統的結構化度量沒有考慮面向對象的一些特性,如繼承、封裝、多態。

繼承對覆蓋率度量的影響

class Base(object): def foo(self): pass def bar(self): pass class Derived(Base): def foo(self): pass case1 = Derived().foo() case2 = Derived().bar() # 要考慮到面向對象的繼承關系 case3 = Base().foo() case4 = Base().bar()

上例中,如果我們的用例不僅要測試到派生類的兩個方法,雖然它背后也調用了基類的方法,但要考慮到繼承的概念,要讓基類自己執行自己的方法。

基於狀態的類的覆蓋率特點

來舉個棧的示例。

class Stack(object): def __init__(self): self.items = [] def push(self, item): self.items.append(item) def pop(self): self.items.pop() p = Stack() p.push('a') # case1 p.pop() # case2

現在,站在灰盒測試的角度來說,通過測試出棧和入棧的操作,已經達到了測試的效果了。

這就完了么?沒有!因為我們沒有考慮到棧的特點,比如,我們有沒有考慮,如果棧為空,你做出棧操作,會不會拋出異常?

如果這是個有邊界限制的棧,棧滿了,你還能做入棧操作嗎?

所以,我們要考慮到棧的各種情況。

根據這些狀態,來針對性寫用例。

最后,從測試方法來說,有白盒、黑盒、灰盒。

當然,在平日沒有必要將彼此划分的很清楚,因為我們學習覆蓋率的目的是用來度量測試完整性的手段,只是驗證測試效果的一種衡量標准。

還是要根據項目、測試階段、測試方法不同,來采用不同的測試覆蓋率。


軟件測試基礎 - 測試覆蓋率


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM