控制流測試
控制流測試(Control Flow Testing):是一種在考慮測試對象的控制流情況下導出測試用例的測試方法,並且借助於控制流圖能評估測試的完整性(覆蓋率)。
原則
- 控制流圖是一個帶有開始節點和結束節點的有向圖
- 程序的指令(語句)是通過節點來表示的
- 一個不帶分支和匯總的語句序列可以簡單地用一個節點來表示
- 語句之間的路徑通過有向線(控制流)表示
- 控制流圖的開始和結束節點在實際應用中常常被省略
測試活動
- 列出應該覆蓋的路徑
- 設計輸入、輸出使得程序能按邏輯含義走到此路徑
- 運行測試來觀察此路徑是否走到。如果沒有走到,則要么是對邏輯理解不足,要么是邏輯本身有錯誤。
主要的控制流測試方法:
- 語句覆蓋
- 分支覆蓋
- 判定覆蓋
- 路徑覆蓋(包括結構化的路徑覆蓋等)
語句覆蓋
語句覆蓋(C0-覆蓋) / 語句覆蓋測試 / 語句測試(Statement testing):在控制流中每條可執行的語句至少被執行一次!
- 必要的、最簡單的,但也是最弱的標准
- 無法檢測到缺失的語句
- 但能發現無法執行到的語句(死代碼)
覆蓋率:
語句覆蓋 = 已執行的語句數目 / 所有語句的總數目 * 100%
語句即節點,控制流圖內的所有節點都走到即為100%語句覆蓋
分支覆蓋
分支覆蓋(C1-覆蓋) / 分支覆蓋測試 / 分支測試(Branch testing):在控制流圖內的每一條邊都至少被執行一次!
- 在實踐中作為最小的測試標准
- 能發現無法執行到的程序分支
- 無法發現缺失的分支
- 沒有測試到分支的組合
覆蓋率:
分支覆蓋 = 已執行的分支數目 / 分支總數目 * 100%
每一條邊即每一條控制流(有向線)都走過了,才為100%分支覆蓋
判定覆蓋
判定覆蓋 / 判定測試(Decision testing):每一個可能的判定輸出都應該被測試到!
- 如果達到100%判定覆蓋,則等價於分支覆蓋
- 但這里計數的不是分支,而是判定的輸出,判定覆蓋在小於100%時,判定的覆蓋率與分支的覆蓋率可能有所不同!
覆蓋率:
判定覆蓋 = 已執行的判定結果 / 所有的判定結果總數 * 100%
判定覆蓋計數的是判定的結果,即布爾值true
和false
的個數
示例代碼:
if (x > y) {
y = x;
}
if (y > z) {
z = y;
}
這段代碼一共有兩個if
判定,每個判定都可以有true
和false
兩個判定結果,一共是四個判定結果。
所以如果只用一條case同時覆蓋x > y
和y > z
,實際上執行到的是兩個判定結果,判定覆蓋率為2 / 4 = 50%
在同一條case的情況下,畫出控制流圖,分支覆蓋一共有9條有向線(包括開始節點和結束節點的兩條有向線),有兩條分支沒有走到,所以分支覆蓋率為7 / 9 = 77.8%
路徑覆蓋
路徑覆蓋(C∞-覆蓋,C4-覆蓋)/ 路徑覆蓋測試 / 路徑測試(Path testing):在控制流圖內的每一個分支序列(路徑)都應該執行一次!
強標准,因為
- 在包含循環時,可能會有無數的路徑
- 在具有k個連續的分支時,則會有2k種不同的路徑
定制選擇有意義的路徑(Beizer)
限制重復次數:循環覆蓋測試方法
限制考慮的路徑:片段對覆蓋(Segment pair)
針對路徑測試,按照Beizer的啟發式方法[Beizer90]:
- 從最簡單的、功能上有意義的路徑開始(從入口到出口的最簡單路徑)
- 每次新的路徑應該只是對現有的微小變化(如果可能,僅只改變一個判定輸出/真值)
- 優先考慮短的路徑、簡單路徑、功能上有意義的路徑、不帶循環的路徑
- 應該達到100%判定覆蓋
- 避免功能上無意義的路徑,如果為了達到判定覆蓋必須執行這路徑,則應該檢查是否是一個錯誤,如果不是,則增加此路徑。
- 優先考慮那些很有可能是實際執行的路徑
從開始節點到結束節點之間全部可能的路徑都走過才是100%路徑覆蓋
- 部分路徑測試經常用在安全關鍵軟件的測試中
- 常用條件覆蓋方法組合使用,因為它們檢查軟件的另一個方面
- 當只需要增加少量的測試用例時,它可以作為分支測試的深入
- 應該使用工具來創建控制流圖
結構化的路徑覆蓋
結構化的路徑覆蓋(Ci(k)-覆蓋):執行在一個內循環中運行次數還沒有超過k次的所有路徑!
對於k = 2時,這種方法也成為內部邊界-路徑覆蓋(Boundary Interior)
僅僅當k為很小值時才有實際意義。
其他針對循環的測試的標准:
- 最小和最大迭代次數,排除迭代數
- 在程序中的循環一般:嵌套、交叉、非結構化的循環
條件測試
- 簡單條件測試
- 條件測試/判定測試 或 判定條件測試
- 改進的條件/判定測試(MC/DC)
- 復合條件測試
它們的區別是測試的深度(從上往下越來越深入),需要的測試用例數,以及能發現多少缺陷
簡單條件測試
簡單條件測試(Condition testing:一個布爾表達式內的每個原子條件(Atomic condition) 都應該對其二個值(真/TRUE 和 假/FALSE)進行測試。
一個原子條件是一個不包含邏輯運算符NOT、AND或OR的條件
如果在程序內的所有條件都僅僅是一個原子條件,則相應的簡單條件測試對應為判定測試。
滿足簡單條件覆蓋不一定會滿足判定覆蓋,所以簡單條件測試僅僅是理論上進行探討,沒有太大實際意義
條件測試/判定測試 或 判定條件測試
**條件測試/判定測試(Decision condition testing):
- 每一個判定都應該要對它的二個值(真和假)進行測試
- 每個原子條件都應該要對它的二個值(真和假)進行測試
包含了簡單條件覆蓋以及判定覆蓋。通常不需要比簡單條件覆蓋更多的測試用例,但是必須謹慎選擇。
- 此方法適合於測試那些重要的,但還不是關鍵和核心的代碼
- 此方法與后面的二種方法相比需要較少的測試用例,但是比單純的判定覆蓋要更繁瑣(還要看原子條件的覆蓋)
改進的條件/判定測試(MC/DC測試)
改進的條件/判定測試(Modified Condition/Decision Coverage)
- 每一個判定都應該要對它的二個真值(真和假)進行測試
- 每一個原子條件都應該對它的二個真值(真和假)進行測試
- 在測試中能夠表明,每一個原子條件的值獨立地影響表達式的結果
針對單個的原子條件,必須關注具有不同結果的測試用例,結對進行比較。
- 先列出所有可能的真值組合
- 選出那些單個原子條件影響結果的測試用例對
- 提供一個更高的控制流覆蓋並能發現比條件覆蓋/判定覆蓋更多的缺陷
- 需要更多的測試用例,但是:
- 當有n個原子條件,通常需要n+1個測試用例。測試用例的數目與條件的數目是線性關系(線性增長)。
- 很適合安全關鍵系統,例如,在航空和航天業廣泛采用改進的條件測試/判定測出(MC/DC)方法
限制:耦合的條件
例如: (A AND B) OR ((NOT A) AND C)
這些條件稱作耦合(重疊),而這些耦合的項往往無法實現MC/DC測試。
解決方法:
- MC/DC僅僅針對不耦合的條件
- 對於含有耦合條件的每個判定作為特例,視不同的情況生成測試用例
限制:短路/精簡的評判(Short-circuiting)
在有條件的進行評價的程序設計語言中往往無法達到MC/DC的覆蓋。
解決方法:必須降低“測試用例對”的要求,對於每個原子條件生成一對測試用例,
- 要覆蓋這個條件的二個真值(真/假)
- 要覆蓋整個判定式的二個真值(真/假)
- 所有其他原子條件具有相同的邏輯或不進行評估。
復合條件測試
復合條件測試(Multiple condition testing):測試原子條件的所有可能的真值(真/假)組合
- 復合條件覆蓋包含了MC/DC覆蓋
- 因為測試用例可以從真值表直接導出,所以測試的設計更為簡單
- 缺點:非常耗資(n個原子條件將會有2n個組合)
- 長期需要穩定可靠的嵌入式系統測試中常會使用此方法(例如,在電話網內的交換機系統,按要求應該運行30年),但也逐漸被MC/DC所替代