java程序員如何編寫更好的單元測試?

在作單元測試時,代碼覆蓋率經常被拿來做爲衡量測試好壞的指標,甚至,用代碼覆蓋率來考覈測試任務完成狀況,好比,代碼覆蓋率必須達到80%或 90%。因而乎,測試人員費盡心思設計案例覆蓋代碼。用代碼覆蓋率來衡量,有利也有有弊。本文咱們就代碼覆蓋率展開討論,也歡迎同窗們踊躍評論。 首先,讓咱們先來了解一下所謂的「代碼覆蓋率」。我找來了所謂的定義:函數

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

上面簡短精悍的文字很是準確的描述了代碼覆蓋率的含義。而代碼覆蓋程度的度量方式是有不少種的,這裏介紹一下最經常使用的幾種:單元測試

1. 語句覆蓋(StatementCoverage)學習

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

這裏舉一個不能再簡單的例子,咱們看下面的被測試代碼:設計

int foo(int a, int b) { return a / b; } 假如咱們的測試人員編寫以下測試案例:ci

TeseCase: a = 10, b = 5it

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

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

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),它度量是否對循環體執行了零次,一次和多餘一次循環。剩下一些其餘覆蓋方式就不介紹了。

總結

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

a. 覆蓋率數據只能表明你測試過哪些代碼,不能表明你是否測試好這些代碼。(好比上面第一個除零Bug)

b. 不要過於相信覆蓋率數據。

c. 不要只拿語句覆蓋率(行覆蓋率)來考覈你的測試人員。

d. 路徑覆蓋率 > 斷定覆蓋 > 語句覆蓋

e. 測試人員不能盲目追求代碼覆蓋率,而應該想辦法設計更多更好的案例,哪怕多設計出來的案例對覆蓋率一點影響也沒有。

相關文章
相關標籤/搜索