代碼覆蓋是一種用來度量已執行的軟件測試水平的方法。收集覆蓋度量數據的過程很簡單:監測您的代碼,并對所監測的版本運行測試。這樣就可以生成相關數據,展示已執行哪些代碼,或者更重要的是,未執行哪些代碼。
大多數開發人員都能理解這一過程,也贊同其價值主張,他們通常追求100%的覆蓋率。盡管100%的覆蓋率是個極好的目標,但類型不當的100%覆蓋率依然會留下未知的問題。典型的軟件開發是根據要測試的語句或分支的數量來度量覆蓋率的。即便有著100%的語句或分支覆蓋率,代碼邏輯依然可能存在嚴重的邏輯bug,只能為開發人員和治理員帶來虛假的安全感。
為何100%的覆蓋率還不夠?這是因為語句和分支覆蓋率無法表明代碼中的邏輯是否已執行。語句和分支覆蓋對于未執行代碼塊中出現的明顯問題來說很有用,但它們經常會錯過與決策結構和決策交互相關的bug。而另一方面,路徑覆蓋則是一種有助于及早發現缺陷的更為健壯和全面的技術。
在了解路徑覆蓋之前,先關注一下語句和分支覆蓋率方面的一些問題:
語句覆蓋可以識別在一個方法或類中執行了哪些語句。這是一種計算起來比較簡單的量規,現在有許多 開源產品 都可以評測這種覆蓋級別。最終,語句覆蓋的獲益在于其能夠識別未執行代碼塊。然而語句覆蓋也存在問題,其無法識別源代碼中控制流結構導致的bug,例如復合條件或者連續開關標簽。這意味著在您可以輕松獲得100%覆蓋率的同時,未發現的明顯bug依然存在。
下例中顯示了這種問題。此處,returnInput()方法由七條語句組成,它有一個簡單的需求:輸出應等于輸入。
圖1. 代碼示例
接下來,您可以創建一個滿足需求并且具有100%語句覆蓋率的JUnit測試用例。

圖2. 語句覆蓋
在 returnInput() 中有一個明顯的bug。假如第一個或第二個決策計算為真而其他的計算為假,返回值則不等于該方法的輸入值。精明的軟件開發人員會立即注重到這個問題,但語句覆蓋報告卻顯示為100%的覆蓋率。假如治理員發現覆蓋率為100%,他/她可能會受到虛假的安全感的影響,判定測試已經完成,繼而發布錯誤百出的代碼,將之投入生產。
僅僅熟悉語句覆蓋是不夠的,開發人員必須進一步使用更為完善的測試技術:分支覆蓋。
分支是指決策的結果,因而分支覆蓋可以評測已測試的決策結果。這聽起來不錯,因為這樣可以比語句覆蓋更深入地查看源代碼,但分支覆蓋也會提出更多的要求。
確定方法中的分支數量非常輕易。布爾決策無疑只有兩種結果:真和假,而開關對于每種情況來說都只有一種結果——別忘了默認情況!方法中的決策結果總數等同于方法中需要覆蓋的分支與輸入分支的總和。(究竟,使用直線代碼的方法也有一個分支)。
在以上示例中, returnInput() 具有七個分支——三個是真,三個是假,還有一個是用于方法輸入的隱藏分支。您可以用兩個測試用例來覆蓋六個真和假的分支:

圖3. 分支覆蓋
兩個測試都驗證了(輸出等于輸入)需求并且達到了100%的分支覆蓋率。但是,盡管實現了100%的分支覆蓋率,測試卻未發現bug。治理員可能會再次認為測試已完成,該方法可進入生產階段。
聰明的開發人員將熟悉到您可能錯過了被測試方法中的一些可能路徑。以上示例已經測試了 真-假-真或假-真-真 路徑,您可以通過添加兩個額外測試來進行檢驗。
該方法中只有三個決策,因此測試所有八種可能路徑是非常輕易的。但對于包含更多決策的方法來說,可能路徑數量將呈指數增長。例如,一個包含十個布爾決策的方法會有1024種可能路徑。這樣只有祝您好運了!
因此,獲得100%的語句和100%的分支覆蓋率是遠遠不夠的,但費力地測試一個復雜方法包含的所有可能路徑也是不可行的。有沒有其他選擇呢?讓我們看看基本路徑覆蓋。
路徑代表著從開始執行方法到退出方法的執行流程。一個有著N個決策的方法會有 2^N 種可能路徑,假如方法中包含一個循環,則會產生無限數量的路徑。幸運的是,您可以使用稱為 圈復雜度(cyclomatic complexity) 的度量來減少需要測試的路徑數量。
新聞熱點
疑難解答