測試覆蓋率之一——測試覆蓋率分類(轉)
關於覆蓋率,網絡上最常見的兩個詞應該是“測試覆蓋率”(Test Coverage)和”代碼覆蓋率“(Code Coverage)。今天就來探探這兩個東西。
在測試里面,一般會將測試覆蓋率分為兩個部分,即”需求覆蓋率“和”代碼覆蓋率“。可以看到,代碼覆蓋率其實是測試覆蓋率的一部分而已。其中,最常討論和關心的是”代碼覆蓋率“,代碼覆蓋率又分為程序語句和代碼行覆蓋,分支覆蓋和條件覆蓋。對於這些概念,我們逐個解釋。
需求覆蓋率:如果需求已經定義好,這個時侯我們就需要考慮需求覆蓋率了。這個時候需要注意的是,這里的需求不僅僅是指功能需求,還要包括性能需求。衡量需求覆蓋率的最直觀的方式是我們有多少功能點,我們有多少性能點要求,這些將作為分母;我們寫了多少測試用例,覆蓋了多少模塊,多少功能點,我們的性能測試用例考慮了待測程序多少性能點,這些作為分子。
代碼覆蓋率:為了更加全面的覆蓋,我們可能還需要測試程序的流程,我們可能會考慮到一個函數的數據的輸入與輸出,甚至是每一行代碼的執行情況,代碼的每一條邏輯和分支,這個時候我們的測試執行情況就以代碼覆蓋率來衡量,這也是我們常在單元測試中念叨的覆蓋率覆蓋率的問題。
語句覆蓋率:換個名字叫做代碼行覆蓋率,這就是監視每行代碼是否在用例(當然之所有的)中是否被執行到,准確點說是我們的用例里面大概執行了百分之多少的語句/代碼行數。需要注意的是,即使所有的語句都被執行到,也不一定執行到了所有的路徑。比如有五條語句:ABCDE,如果我們執行了用例覆蓋了ABCDE,另外一個用例這個時候我們覆蓋了所有語句,但是可能還存在一個路徑(如ABC)沒有執行,例如:
public verifyToken(string yourname, string yourtitle)
{
A output(”Hello, my dear friends“);
B if(yourname == "uniquestudiowcd")
{
C output("Hello, Aaron");
}
D if(yourtitle == "tester")
{
E output("Hello, my dear tester");
}
}
這個時候我們輸入參數”uniquestudiowcd“和”tester“覆蓋到了所有的語句,但是我們漏掉了一個路徑:即輸入參數”uniquestudiowcd“和”coder“。
分支覆蓋率:我們也給它換個名字即”路徑覆蓋率“,盡管並不完全對。在上面的例子中,如果我們僅考慮了第一個用例(即輸入參數”uniquestudiowcd“和”tester“),我們的語句覆蓋率為100%,帶式路徑覆蓋率可就低了,因為它存在ABD,ABCD,ABCDE,ABDE等等很多路徑。
條件覆蓋率:這也就是為什么不能說”分支覆蓋“不同於”路徑覆蓋“的原因所在。如果我們在一個IF語句中加入了判斷組合,那就要考慮更多的問題了,因為主要出現在條件語句中,所以我們稱之為”條件覆蓋“。我們更改上述示例代碼:
public verifyToken(string yourname, string yourtitle,string gendar)
{
A output(”Hello, my dear friends“);
B if(yourname == "uniquestudiowcd" && gendar == ”man“)
{
C output("Hello, Aaron");
}
D if(yourtitle == "tester")
{
E output("Hello, my dear tester");
}
}
很明顯即使我們輸入參數”uniquestudiowcd“”tester“,”woman“和”uniquestudiowcd“”tester“”man“,這兩個用例的路徑走的分支是一樣的,但是條件覆蓋不一樣,實際上兩者的”路徑“也是不一樣的。
上面主要介紹的測試覆蓋率的一些基本知識,在關於測試覆蓋率的第二篇文章中,我將介紹歸納一下測試覆蓋率的用處,或者說測試覆蓋率的意義。
測試覆蓋率之二——測試覆蓋率有什么用?
在上一篇文章里面我們介紹了測試覆蓋率的分類,舉例揭示了需求覆蓋率,語句覆蓋率,分支覆蓋率很條件覆蓋率這些問題,在這篇文章里面,則主要介紹為什么要千方百計來找“測試覆蓋率”。(關於上一篇文章,參見測試覆蓋率之一——測試覆蓋率分類)
關於測試覆蓋率講的最多的地方應該實在測試停止標准里面。在測試停止標准里面經常出現這樣的語句“測試覆蓋率達到或超過95%”之類的概念。其實,如果你看了我前一篇文章中提到的測試覆蓋率分類的話,就知道這是一個不准確的描述。關於更准確的描述,我認為應該是“性能測試需求覆蓋率達到100%,功能需求測試覆蓋率達到100%,語句覆蓋率達到85%”這樣的句子。“測試覆蓋率”本來就包含了很多子部分,所以提測試覆蓋率是不明智的一種做法。而我所說的語句覆蓋率85%相對於性能測試需求覆蓋率這個數據來講似乎更難獲得准確數值,不過現在已經有了很多工具用於測試“語句覆蓋率”,而不用我們自己去計算已執行的測試用例覆蓋到了數萬或者更多代碼中百分之多少,也有一些工具可以幫助我們得到代碼覆蓋率中“分支覆蓋率”等其它數據。關於覆蓋率檢測工具,我將在本系列的后續文章中給予介紹。
測試覆蓋率是測試結束標准中的一部分,這顯然不是我們今天討論的重點——測試覆蓋率有什么用?直觀上講,我們可以這樣理解:
-> 性能測試覆蓋率如果沒達到100%,表示我們有些性能測試點沒有覆蓋到,這在一個對於性能有所要求系統顯然是不可取的,這表示我們應該增加用例來覆蓋到所需要的性能測試點。
-> 重要模塊的語句覆蓋率和條件覆蓋率很低,表示我們測試用例過少,我們應當增加用例;如果我們已經寫了很多用例(相對於代碼行數來講),但是這兩項數據還是很低,那我們得檢查一下我們的用例了,是否有重復的用例?是否應該重新設計用例結構?
對於測試覆蓋率,我們有這樣一個簡單的算式,如果A模塊的條件覆蓋率為80%,B模塊也為80%,C模塊也為80%,那么我們的總覆蓋率則是 51.2%,而不是我們想當然的80%。至於為什么這樣,我就不解釋了。因此在我們審查覆蓋率報告時候,我們關注的是覆蓋率低的模塊,我們要檢查為什么低,我們要思考怎么提高,對於覆蓋率低的地方,是不是有一個等價類被我們忽略了?
測試覆蓋率的意義在瀑布式的開發模式里面可能顯得沒那么重要,但是如果在螺旋式開發模式中,如果我們沒有控制好我們上一個迭代中的測試覆蓋率,當一個版本一個版本累加下來后,你就很難確定我們哪些模塊在開發過程中沒有給予足夠的測試;在近些年興起的持續集成浪潮中,由於要求短迭代(有人建議3-5天一個迭代),如果沒有很好的測試覆蓋率保證,很難在這么快的代碼變遷中保證測試的質量。在持續集成工作中,代碼提交頻繁,我們可以通過測試覆蓋率來了快速對應新寫的,沒有對應測試的代碼。
總之,測試覆蓋率可以提供給我們兩個方面的信息:測試覆蓋率低的模塊 和重要模塊的測試覆蓋率。這些數據可以幫助我們快速定位需要更多測試的模塊,可以幫助我們了解重要模塊的測試情況,以此來衡量我們測試用例的質量乃至測試的質量。
測試覆蓋率之三——測試覆蓋率100%相關的話題
上一篇文章中,介紹了測試覆蓋率的意義之類的東西。測試覆蓋率可以幫助我們檢查測試質量,檢查測試用例的有效率。如果有興趣的話,可以閱讀測試覆蓋率之二——測試覆蓋率有什么用?
關於測試覆蓋率,我個人的感覺是說的多,用的少。最近在網絡上看到一篇文章,討論一個問題“測試需要100%的覆蓋率嗎?”被轉載了很多次,有興趣的同行可以找來看看。的確,一想到測試覆蓋率,立馬就有完美主義者跳出來說100%。100%的測試覆蓋率有什么好處呢?
1、100%的覆蓋率表示我們的測試覆蓋到了所有語句,分支,條件
2、100%的覆蓋率表示我們測試考慮的很完全,我們可以回去睡大覺了~~
測試仿佛在這里變得不那么可怖了,但是我們至少遺漏了兩個重要的地方:怎么達到100%的測試覆蓋率或者說是否能夠達到100%的測試覆蓋率,另外一個就是100%的測試覆蓋率到底能告訴我們什么信息。
首先來講,我們是否可以達到100%的測試覆蓋率?如果我們簡單的將測試覆蓋率理解為需求覆蓋率,代碼覆蓋率,那么我想這是可以達到的,只要擁有足夠的時間,我們的測試覆蓋到每一個需求點,我們的測試覆蓋到每一條語句,每一個條件,每一個分支,看起來的確沒有問題。但是我們還要考慮另外一個問題,是否由我們未曾列入到需求分析中的需求呢,這種情況是存在的,如果我們計算需求覆蓋率是根據Feature Spec來的(實際上如果我們需要計算的話,一般就是這樣計算得來的),那么當我們有需求沒有被寫入Feature Spec並且我們也沒有在測試中考慮相關的測試,那么我們實際的“需求覆蓋率”就不是100%了。在實際開發過程中,是不可能在Feature Spec中將需求全部列出來的,所以我們得到的100%的需求覆蓋率是存在水分的。
另外,對於一個應用程序(除了一些極其簡單的程序)來講,要覆蓋到所有的語句。條件,分支是極其困難的,甚至可以說是不可能的。筆者在經歷的一個項目中花了一整天寫一個模塊的單元測試,當我忙完一天並運行了所有的用例之后,我發現我的代碼覆蓋率僅僅增加了2%,而且是從35%到37%,不要說100%,連80%我當時都覺得是奢望。
對於第二個問題,100%的測試覆蓋率能代表什么?我在上面講到,100%的測試覆蓋率表示覆蓋到了所有的語句,分支和條件,但是這又說明什么呢?這是否說明了我們做到了完全測試一個軟件呢?很抱歉,答案是否定的。給出下面這一段代碼:
private int add(int a,int b)
{
return a+b;
}
夠簡單的一段代碼了吧,我們可以很輕松的達到100%的覆蓋率,比如我們使用用例 add(3,4)就可以覆蓋所有的語句,分支,條件(當然這里面是不存在分支和條件的,所以只需要覆蓋語句就可以達到代碼覆蓋率100%了),但是聰明的你一定會發現我們的測試遠遠不夠:如果輸入的是add(2147483647,2),這個應用程序是會出現問題的,如果我們僅僅滿足於100%的代碼覆蓋率,是不能保證我們的軟件的質量的。
關於代碼覆蓋率,由一個很有趣的現象:高覆蓋率有時候比低覆蓋率還“沒用”。注意“沒用”是打了引號的,我的意思是高覆蓋率不能說明我們做了完全的測試,低覆蓋率卻可以說明我們測試遠遠不夠,從這一點來講,低覆蓋率似乎更有意義。當然我不是在講我們不去追求高覆蓋率,我的意思是與其把A模塊覆蓋率從85% 提高到90%,還不如把與其類似的B模塊的覆蓋率從30%提高到50%更有意義。繞一大圈說回來,在任何時候高覆蓋率都比低覆蓋率好,但是作為一個軟件,我們要顧及軟件整體的測試質量,我們還要估計成本,時間等等很多問題。
上面說了不少,最后總結一下我的觀點:
1、測試覆蓋率100%是一個理想的情況,是很難達到的;
2、測試覆蓋率100%不能說明我們做了完全的測試;
3、較低的測試覆蓋率能說明我們的測試還不夠,反之是不成立的,參考第二條;
4、同一模塊高覆蓋率相對於低覆蓋率能說明我們做了更多的測試;
5、測試覆蓋率達到多少要考慮到軟件整體的覆蓋率情況,以及項目成本,包括人力,時間等等。
關於測試覆蓋率100%的問題的討論還會繼續下去,如果必要的話,筆者將在本系列文章的后期繼續總結,根據計划,在下一篇文章中我將介紹自己使用過的相關工具,以及我未使用但是可以從網上找到相關資料的工具,幫助大家總結一下,以備查看。
測試覆蓋率之四——測試覆蓋率工具匯總
在上一篇文章我提到的是關於測試覆蓋率100%有關的話題,算是“跟風”談論了最近關於測試覆蓋率最流行的100%問題吧。關於上篇文章的詳細內容,參見測試覆蓋率之三——測試覆蓋率100%相關的話題。
在上一篇文章中,和大家約定下一篇介紹關於測試覆蓋率工具相關的東西,可是這兩天一直出差,無暇顧及,希望關注我的朋友不要介意~ _ ~ 廢話不說了,直接切入正題。由於本人對於測試覆蓋率工具的使用僅限於.NET相關的,所以對於其他語言相關的測試覆蓋率工具沒有經驗,因此也少了發言權,這片文章就只能算作對於各種工具的一種簡單的介紹罷了,主要內容都來自於google百度,筆者做簡單的整理之后發表出來,希望對大家有所幫助。
● Javascrīpt 測試覆蓋率工具
JSCoverage是一個用於度量Javascrīpt程序的代碼覆蓋率的工具。能顯示哪些行被執行過了,哪些行尚未執行,這些信息對於測試覆蓋率的分析和測試質量的衡量都很有用。JSCoverage通過度量Web頁面使用的Javascrīpt代碼,收集被Web瀏覽器執行的Javascrīpt代碼信息來達到測試覆蓋率統計的功能。JSCoverage支持IE6、IE7、Firefox2、Firefox3、Opera、Safari等流行的瀏覽器、支持Windows平台和Linux平台。JSCoverage是開源軟件,官方網站:http://siliconforks.com/jscoverage/
● Java測試覆蓋率工具
EMMA,開源工具,支持Java 1.2或更高版本的JVM,不依賴於任何第三方類庫。EMMA支持maven,ant,報表格式簡單。官方網站 http://emma.sourceforge.net/
Coverlipse,一個Eclipse的Code coverage插件。
Cobertura 是一種開源工具,它通過檢測基本的代碼,並觀察在測試包運行時執行了哪些代碼和沒有執行哪些代碼,來測量測試覆蓋率。除了找出未測試到的代碼並發現 bug 外,Cobertura 還可以通過標記無用的、執行不到的代碼來優化代碼,還可以提供 API 實際操作的內部信息。
Clover
NoUnit
● .NET測試覆蓋率工具
Clover.NET http://www.cenqua.com/clover.net/
Visual Studio的代碼覆蓋率統計工具
NCover官方網站:http://ncover.org/
PartCover
● C/C++測試覆蓋率工具
Bullseye Coverage 是Bullseye 公司提供的一款C/C++代碼覆蓋率測試工具除了支持各種Unix 下的編譯器之外,在Windows 下支持VC、Borland C++、Gnu C++、Inter C++。提供的代碼覆蓋率是分支覆蓋率而不是一般代碼覆蓋率,我個人認為分支覆蓋率比代碼覆蓋率更好。Bullseye Coverage 可以從http://www.bullseye.com/上獲取
● Ruby代碼覆蓋率工具
rcov是一個用於診斷Ruby代碼覆蓋率的工具,它最主要的用途就是用於確定單元測試是否覆蓋到了所有代碼,rcov使用一個經過優化的C運行時,因此性能相當驚人,同時它還提供多種格式的輸出
● 其他
AutomatedQA公司的AQTime。AQtime運行在windows平台下,它支持.net應用和非.net應用,但不支持JAVA應用。 AQtime除了包含代碼覆蓋率監測以外,還包括了性能監視等功能。AQTime能夠收集服務端C#和VB.net代碼的覆蓋率,但是不能收集客戶端scrīpt腳本的覆蓋率。
DevPartner Studio的Web scrīpt Coverage工具。該工具主要是收集Web客戶端scrīpt腳本覆蓋率的。 它使用起來也很簡單,只要啟動此工具,然后在瀏覽器中輸入網址,收集工作就開始了。在形成的測試報告中清楚地反映了每個函數的實行情況,給出了覆蓋率數據,同時對於執行到的腳本和未執行到的腳本用不同的顏色表示,十分明了。該工具唯一的缺陷就是不能收集服務端腳本的覆蓋率,同時存在中文字符無法正確識別的問題。
關於測試覆蓋率工具,有很多內容,上面提到的只是我平時收集到的一些知識,很大一部分並沒有實際驗證,因此對於可能出現的紕漏和錯誤,還望讀者原諒。關於測試覆蓋率工具,筆者很有興趣繼續學習使用,並會在后期的學習中總結並發表在該系列文章中。在本系列的下一篇文章(測試覆蓋率之五——提高測試覆蓋率)中,筆者將繼續探討有關提高測試覆蓋率的問題。
測試覆蓋率之五——提高測試覆蓋率
在上一篇文章中,簡單的而介紹了一些測試覆蓋率相關的工具,由於大部分工具筆者並沒有使用的經驗,因此只是簡單地從網絡搜索了一下相關資料並將其整理出來,關於上篇文章的詳細內容,參見:測試覆蓋率之四——測試覆蓋率工具匯總。
這篇文章中,主要討論的是如何提高測試覆蓋率的相關問題。其實,提高測試覆蓋率最基本,甚至是唯一的辦法就是增加測試用例,但是怎樣通過增加測試用例而幫助我們“迅速”提高我們的測試覆蓋率呢?
代碼走查 對於代碼的不熟悉造成了我們的代碼覆蓋率遲遲上不去,我們需要了解到代碼里面究竟有多少條件分支,多少怎樣的循環,分支和循環向來是導致我們代碼覆蓋率比較低的原因,另外,是不是存在一些過時的代碼,沒有運行過代碼監測工具的代碼中很可能存在一些沒有被引用的死代碼,而代碼走查,尤其是對於覆蓋率低的模塊的代碼走查將有助於你增加相關的用例而提高代碼覆蓋率。
工具 這里倒不是說有些工具可以幫助你直接提高代碼覆蓋率,這樣的工具至少我還沒有就見過。這里提到的工具主要包括兩種,一是代碼分析工具另外一種就是上篇文章中提到的代碼覆蓋率工具。代碼分析工具可以幫助我們分析代碼中的冗余部分,這樣可以幫助我們干掉那些總是不可能覆蓋到的死函數,有的編譯器已經提供了類似的功能。使用代碼覆蓋率工具則可以幫助我們快速監測代碼覆蓋率低的地方,這樣我們可以快速定位我們測試的薄弱環節,通過代碼走查或其他方式可以快速增加用例。一般來講,某一模塊的代碼覆蓋率從30%提高到50%所需的時間遙遠小於從60%提高到80%的時間。
規則 這里所指的規則其實是指一些基本測試方法,如等價類划分,邊界值分析。我們有時候需要通過這些手段來逐一檢查我們那些方面的測試用例沒有考慮到,從而幫助我們增加相關的測試用例。
經驗 這個看起來有點像廢話,因為一般都知道測試經驗豐富有助於測試用例的設計,寫出別人沒有想到的測試用例。我在這里把這句話提出來的主要意圖是告訴大家注意平時的積累,某些時候的靈光一現可能成為日后的一個重要用例。以前的失敗教訓也可以幫助我們從中學到經驗,畢竟“經驗是人為其錯誤而找的代名詞”。
注意 有一點我想有必要再次提醒大家:單方面的提高測試覆蓋率並不能有效的幫助測試質量的提升,尤其是在代碼質量低劣的情況下。就拿一個經典的三角形測試用例來講,開發人員的代碼可能僅僅判斷了“兩邊之和大於第三邊”然后就返回“這是三角形”。在測試用例中,我們可能考慮了很多的問題,考慮了輸入數據的類型,合法性等等的問題,但是這並無助於增加測試覆蓋率。運行這些測試結果是萬里江山一片紅,記住在你的測試用例沒有運行通過的時候考慮測試覆蓋率是沒有意義的,我目前想到了兩條理由:一是這些測試用例可能在代碼的中間部分就已經出了問題,所以用例本該覆蓋到語句沒有覆蓋到,這降低了代碼覆蓋率數據;第二個理由測試用例沒有通過可能就如剛才提到的三角形問題中一樣,開發人員壓根就沒有那么多語句給你去覆蓋,這時候的代碼覆蓋率數據顯然是沒有多大作用的。這也印證了前面文章中提到的“高代碼覆蓋率比低代碼覆蓋率更加‘沒用’”。
寫到這里,我的觀點已經表達完畢了。這一系列文章也差不多可以做個了結了。當然我們還留了一些重要的"尾巴"——測試覆蓋率工具。在昨天的文章中提到,筆者將實際學習使用一些工具,並將整理相關的資料,當然我是不會爽約的,不過這些內容恐怕要等一段時間才能開始了。