[測試]Code Coverage

  • 21196
  • 0
  • 2012-04-20

[測試]Code Coverage

前言
承接著之前單元測試相關的文章中,提到了程式的可測試性為系統重要的品質指標之一。這篇文章要提的,則是也很常用來當作檢視測試的品質指標之一。

測試不是有做就好,做心安的...透過code coverage,可以快速的得知,目前系統中,有多少程式被測試過,以及測試的過程中,程式執行的路徑為何。

Code Coverage的定義
基本上就是測試程式碼的涵蓋範圍,不考量成本跟投資效益比,涵蓋率越高,當然代表系統如預期正常運作的面也會比較廣。

Code Coverage的種類
Code Coverage種類不少,以下舉出幾種:

  1. Function Coverage
    以function為單位,檢測有哪些function曾經被測試程式測試過。只要有測試到該function,就當作測過了。假設production code裡面有10個function,只有1個被測試過,那function coverage就是10%。
     
  2. Line Coverage
    也就是是否production code的每一行程式碼,都有被測試程式執行到。也是一般.NET的code coverage工具裡面,最常看到的涵蓋率。

    如果,你以為每一行都有跑到,Line Coverage 100%就代表這是測試品質的最高表現,那就錯了,Line Coverage只是幾個涵蓋率分類裡面的子集合而已。
     
  3. Statement Coverage
    與line Coverage一行一行用斷行符號來分的切入角度不同,而是使用statement來當檢測單位。在許多的程式語言中,是允許一行裡面出現多個statement的,當一行裡面擁有多個statement,而測試程式中,只有檢測到其中一個statement,那麼line coverage會涵蓋到,statement coverage卻可以抓出其中涵蓋度不足的部分。
     
  4. Decision Coverage
    就像決策樹一般,每一個決策分歧點,也就是程式執行路徑,為檢測單位。值得一提的是,Decision coverage針對的是分歧後的結果。然而,Decision Coverage可以涵蓋到Statement coverage的死角,舉個例子:
    
            public double Sample(double a, double b, double x)
            {
    
                if (a > 0 && b == 0)
                {
                    x = x / a;
                }
    
                if (a == 2 || x == 1)
                {
                    x = x / b;
                }
    
                return x;
            }


    statement coverage要100%,test case只需要a=2, b=0, x=0,即每一個statement都會跑到。但一樣每一個statement都測到了,就代表這個測試完美了嗎?no, 因為判斷式的條件有多個,判斷式裡面的陳述式所回傳的bool值,可能發生意想不到的錯誤。
     
  5. Condition Coverage
    即使是Decision Coverage,也有不足之處。不足處在於Decision針對的是每條不同路徑要測試的程式主體,但判斷式本身,卻不過問。而Condition Coverage是連判斷式裡面的陳述式(sub-expression),每一種陳述式所回傳的bool正反兩種情境都要測到。

    以wiki上的例子來說:
    
    if a and b then  

    只要測試『a=true, b=false』與『a=false, b=true』就能滿足Condition Coverage。
     
  6. Condition/Decision Coverage
    沒錯!就是摻在一起做撒尿牛丸。除了判斷式中每個陳述式的bool都要測true/false,也要測試各種路徑裡面的各種結果。
     
  7. Modified condition/decision coverage
    可以說是Condition/Decision Coverage的完成體,一樣以wiki上的例子來說:
    
    if (a or b) and c then

    要滿足Condition Coverage (Decision就只是測試判斷式的大括號肚子裡面的內容,這邊先略過),基本上只要測試『a=true, b=true, c=true』與『a=false, b=false, c=false』,也就是三種陳述式的true/false都測到了。

    但,有測試到最後組合出來的結果嗎?
    以這個例子來說,整個if陳述式由兩個子陳述式所組成,一個是 (a or b),一個是c。

    先看第一個(a or b),要測試整個陳述式結果為true跟false,而(a or b)又有a與b兩個子陳述式。

    所以,要滿足各種可能,則需測試:
    1. a=false, b=false, c=true => 第一個陳述式為false,第二個陳述式為true,and結果為false
    2. a=true, b=false, c=true => 第一個陳述式為true,第二個陳述式為true,and結果為true
    3. a=false, b=true, c=true => 第一個陳述式為true,第二個陳述式為true,and結果為true
    4. a=true, b=true, c=false => 第一個陳述式為true,第二個陳述式為false,and結果為false

      也就是組合條件,去測到每一個子陳述式的true/false。
       
  8. Multiple Condition Coverage
    最後當然就是最瘋狂的一種,每一個陳述式都有true/false,去做2的n次方種組合。

    以上面的例子就是:
    1. a=false, b=false, c=false
    2. a=false, b=false, c=true
    3. a=false, b=true, c=false
    4. a=false, b=true, c=true
    5. a=true, b=false, c=false
    6. a=true, b=false, c=true
    7. a=true, b=true, c=false
    8. a=true, b=true, c=true


Code Coverage的工具
很抱歉的是,我知道的都是需要收費的。一個open source不用收費的PartCover(簡介),作者沒有再維護了。(這就是open source現實的地方)

其他收費的工具,請參考wiki上的介紹:

Tools for C# .NET
DevPartner
JetBrains dotCover
Kalistick
NCover
TestDriven.NET
Visual Studio 2010


補充工具
Microsoft Research有出一個很棒的東西叫做Pex and Moles,可以讓你很『快速』的產生高程式碼覆蓋率的單元測試程式,且支援多種Unit Test Framework。這邊的快速,指的是可以省去我們思考不同的test case滿足不同路徑的時間,但Pex還是需要時間、CPU去計算出滿足所有路徑的input/output。

針對重要的Scenario,可以透過它來補足測試遺漏的地方。

它也有個有趣的網頁,是可以讓我們寫程式,讓Pex來跑所有可能性,請參考:PexForFun

Pex與VS2010整合得很好,最上面的例子,跑完Pex parse出來的結果會像下圖這樣:
PexParse

產生的單元測試碼,請見下圖:
UnitTestByPex

自動產生的test case,請見下圖:
PexTestCases

結論
Code Coverage的迷思
上面介紹了這麼多種Code Coverage的分類,幾乎是一個比一個還嚴苛,您覺得呢?我只能說,這太遙不可及了,多少人連單元測試都還沒寫過。越往下的測試分類所花的成本越高,投資效益比越低。

即使只有line coverage,我也認為足夠用了。什麼叫做足夠用,除非是寫一個完完全全不容許任何差錯,沒有bug,沒有絲毫遺漏的系統。不然,系統多少都有bug,多少都得fix bug。NASA發射太空梭都有失敗的了,何況我們的程式。

請把code coverage的數字當作一個健康指標 (甚至稱不上系統品質指標,勉強稱得上是測試的品質指標),用來檢查什麼?

  1. 有沒有做單元測試?
  2. 重要的Scenario有沒有涵蓋多一點情境?
  3. 現在有哪些程式碼沒有被測過?
  4. 發生測試失敗時,失敗的原因點production code是否有被test case涵蓋到?

夠了,這樣就很夠了。

請把更多的時間,花在如何寫出具有可測試性的production code,如何落實Unit Test,以及如何用最少的成本,最低的阻力建立自動化的環境。重點在於,如何讓每一次的重構、每一次的需求異動、每一次的bug,都能快速且準確的增加對應的Unit Test,才是要中之要。

2012/04/20:補充Martin Fowler的文章:TestCoverage

Reference

  1. http://en.wikipedia.org/wiki/Code_coverage
  2. http://blog.udn.com/chungchia/3341216
  3. http://www.51testing.com/?uid-3733-action-spacelist-type-blog-itemtypeid-9157,動態圖片解說很讚

blog 與課程更新內容,請前往新站位置:http://tdd.best/