單元測試代碼覆蓋率的淺談


  在做單元測試時,代碼覆蓋率通常被拿來作為衡量測試好壞的指標,甚至,用代碼覆蓋率來考核測試任務完成情況。比如,代碼覆蓋率必須達到80%或90%。於是乎,測試人員費盡心思設計案例覆蓋代碼,用代碼覆蓋率來衡量,有利也有弊。本文我們就代碼覆蓋率展開討論。

  首先,讓我們先來了解一下所謂的“代碼覆蓋率”。我找來的所謂的定義:

  代碼覆蓋率 = 代碼的覆蓋程度,一種度量方式

  上面簡短精悍的文字非常准確的描述了代碼覆蓋率的含義。而代碼覆蓋程度的度量方式是有很多種的,這里介紹一下最常用的幾種:

  1.語句覆蓋(StatemenCoverage)

  又稱行覆蓋(LineCoverage),段覆蓋(SegmentCoverage),基本塊覆蓋(BasicBlockCoverage),這是最常用也是最常見的一種覆蓋方式,就是度量被測代碼中每個可執行語句是否被執行到了。這里說的是“可執行語句”,因此就不會包括像C++的頭文件聲明,代碼注釋,空行,等等。非常好理解,只統計能夠執行的代碼被執行了多少行。需要注意的是,單獨一行的花括號{} 也常常被統計進去。語句覆蓋常常被人指責為“最弱的覆蓋”,它只管覆蓋代碼中的執行語句,卻不考慮各種分支的組合等等。假如你的上司只要求你達到語句覆蓋,那么你可以省下很多功夫,但是,換來的確實測試效果的不明顯,很難更多地發現代碼中的問題。

  這里舉一個不能再簡單的例子,我們看下面的被測試代碼:

int foo(int a, int b)
{
   return  a / b;
}

  假如我們的測試人員編寫如下測試案例:

TeseCase: a = 10, b = 5

  測試人員的測試結果會告訴你,他的代碼覆蓋率達到了100%,並且所有測試案例都通過了。然而遺憾的是,我們的語句覆蓋率達到了所謂的100%,但是卻沒有發現最簡單的Bug,比如,當我讓b=0時,會拋出一個除零異常。

  正因如此,假如上面只要求測試人員語句覆蓋率達到多少的話,測試人員只要鑽鑽空子,專門針對如何覆蓋代碼行編寫測試案例,就很容易達到主管的要求。當然了,這同時說明了幾個問題:

  1. 主管只使用語句覆蓋率來考核測試人員本身就有問題。
  2. 測試人員的目的是為了測好代碼,鑽如此的空子是缺乏職業道德的。
  3. 是否應該采用更好的考核方式來考核測試人員的工作?

  為了尋求更好的考核標准,我們必須先了解完代碼覆蓋率到底還有哪些,如果你的主管只知道語句覆蓋,行覆蓋,那么你應該主動向他介紹還有更多的覆蓋方式。比如:

  2.判定覆蓋(DecisionCoverage)

  又稱分支覆蓋(BranchCoverage),所有邊界覆蓋(All-EdgesCoverage),基本路徑覆蓋(BasicPathCoverage),判定路徑覆蓋(Decision-Decision-Path)。它度量程序中每一個判定的分支是否都被測試到了。這句話是需要進一步理解的,應該非常容易和下面說到的條件覆蓋混淆。因此我們直接介紹第三種覆蓋方式,然后和判定覆蓋一起來對比,就明白兩者是怎么回事了。

  3.條件覆蓋(ConditionCoverage)

  它度量判定中的每個子表達式結果true和false是否被測試到了。條件覆蓋針對判斷語句里面案例的取值都要去一次,不考慮條件的取值。為了說明判定覆蓋和條件覆蓋的區別,我們來舉一個例子,假如我們的被測代碼如下:

int foo(int a, int b)
{
    if (a < 10 || b < 10) // 判定
    {
        return 0; // 分支一
    }
    else
    {
        return 1; // 分支二
    }
}

  設計判定覆蓋案例時,我們只需要考慮判定結果為true和false兩種情況,因此,我們設計如下的案例就能達到判定覆蓋率100%:

TestCaes1: a = 5, b = 任意數字  覆蓋了分支一
TestCaes2: a = 15, b = 15          覆蓋了分支二

  設計條件覆蓋案例時,我們需要考慮判定中的每個條件表達式結果,為了覆蓋率達到100%,我們設計了如下的案例:

TestCase1: a = 5, b = 5       true,  true
TestCase4: a = 15, b = 15   false, false

  通過上面的例子,我們應該很清楚了判定覆蓋和條件覆蓋的區別。需要特別注意的是:條件覆蓋不是將判定中的每個條件表達式的結果進行排列組合,而是只要每個條件表達式的結果true和false測試到了就OK了。因此,我們可以這樣推論:完全的條件覆蓋並不能保證完全的判定覆蓋。比如上面的例子,假如我設計的案例為:

TestCase1: a = 5, b = 15  true,  false   分支一
TestCase1: a = 15, b = 5  false, true    分支一

  我們看到,雖然我們完整的做到了條件覆蓋,但是我們卻沒有做到完整的判定覆蓋,我們只覆蓋了分支一。上面的例子也可以看出,這兩種覆蓋方式看起來似乎都不咋滴。我們接下來看看第四種覆蓋方式。

  4.路徑覆蓋(PathCoverage)

  又稱斷言覆蓋(PredicateCoverage)。它度量了是否函數的每一個分支都被執行了。 這句話也非常好理解,就是所有可能的分支都執行一遍,有多個分支嵌套時,需要對多個分支進行排列組合,可想而知,測試路徑隨着分支的數量指數級別增加。比如下面的測試代碼中有兩個判定分支:

int foo(int a, int b)
{
    int nReturn = 0;
    if (a < 10)
    {// 分支一
        nReturn += 1;
    }
    if (b < 10)
    {// 分支二
        nReturn += 10;
    }
    return nReturn;
}

  對上面的代碼,我們分別針對我們前三種覆蓋方式來設計測試案例:

  a. 語句覆蓋

TestCase a = 5, b = 5   nReturn = 11
 //語句覆蓋率100%

  b. 判定覆蓋

TestCase1 a = 5,   b = 5     nReturn = 11
TestCase2 a = 15, b = 15   nReturn = 0
//判定覆蓋率100% 

  c. 條件覆蓋

TestCase1 a = 5,   b = 15   nReturn = 1
TestCase2 a = 15, b = 5     nReturn = 10
//條件覆蓋率100% 

  我們看到,上面三種覆蓋率結果看起來都很酷!都達到了100%!主管可能會非常的開心,但是,讓我們再去仔細的看看,上面被測代碼中,nReturn的結果一共有四種可能的返回值:0,1,10,11,而我們上面的針對每種覆蓋率設計的測試案例只覆蓋了部分返回值,因此,可以說使用上面任一覆蓋方式,雖然覆蓋率達到了100%,但是並沒有測試完全。接下來我們來看看針對路徑覆蓋設計出來的測試案例:

TestCase1 a = 5,    b = 5     nReturn = 0
TestCase2 a = 15,  b = 5     nReturn = 1
TestCase3 a = 5,    b = 15   nReturn = 10
TestCase4 a = 15,  b = 15   nReturn = 11
//路徑覆蓋率100% 

  太棒了!路徑覆蓋將所有可能的返回值都測試到了。這也正是它被很多人認為是“最強的覆蓋”的原因了。

  還有一些其他的覆蓋方式,如:循環覆蓋(LoopCoverage),它度量是否對循環體執行了零次,一次和多余一次循環。剩下一些其他覆蓋方式就不介紹了。

  總結

  通過上面的學習,我們再回頭想想,覆蓋率數據到底有多大意義。我總結了如下幾個觀點,歡迎大家討論:

  • 覆蓋率數據只能代表你測試過哪些代碼,不能代表你是否測試好這些代碼。(比如上面第一個除零Bug)
  • 不要過於相信覆蓋率數據。
  • 不要只拿語句覆蓋率(行覆蓋率)來考核你的測試人員。
  • 路徑覆蓋率 > 判定覆蓋 > 語句覆蓋
  • 測試人員不能盲目追求代碼覆蓋率,而應該想辦法設計更多更好的案例,哪怕多設計出來的案例對覆蓋率一點影響也沒有。

   補充:java單元測試原則

  1. 測試代碼放在src/test/java路徑下。
  2. 測試類以*Test.java結尾,測試方法以test*開頭。
  3. 測試用例的書寫遵循O2O代碼規范。
  4. 所有public,protected方法必須被單元測試覆蓋。
  5. 外部接口調用,以及數據庫和其他中間件的訪問都使用mock。
  6. 一個測試類只對應一個被測類,如測試類 DemoTest.java 測試Demo.java
  7. 一個測試方法只測試一個方法,方法testFoo() 測試 foo()方法。
  8. 一個測試用例只測試一個功能。
  9. 測試用例不要使用@ignored或者被注釋掉。
  10. 測試用例不應當依賴環境。
  11. 測試用例輸入要具體,不應當出現例如System.currentTimeMillis()
  12. 測試用例必須具有確定的預測結果
  13. 保持測試用例是冪等的。

 

原文


免責聲明!

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



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