在軟件行業里,幾乎所有的開發人員都在談代碼質量,而每個人對代碼質量都有一套自己的看法。甚至術語代碼味道(code smell) 也已進入大眾詞匯表,成為描述代碼需要改進的一種方式。
代碼味道是由我們開發人員根據自己的一些工作經驗積累來判斷的,有人覺得代碼注釋可以體現代碼結構和質量,還有些人又認為代碼注釋是用來解釋過于復雜代碼的一種說明機制。顯然,Javadocs™ 很有用,但是多少內嵌注釋才足以維護代碼?如果代碼已經編寫得足夠好,它還需要自己解釋嗎?從這些我們可以看出代碼味道是一種主觀評估的機制,在很多情況下面,盡管一些看其來糟透了的代碼可能是他人曾經編寫最好的代碼。在我們工作中,是否有很多這樣的聲音,”是的,初看起來有點亂,但是它的擴展性不錯”。
因此,我們需要客觀評估代碼質量的方法,某種可以決定性地告訴我們正在查看的代碼是否存在風險的東西。不管您是否相信,這種東西確實存在!用來客觀評估代碼質量的機制已經出現了一段時間了,只是大多數開發人員忽略了它們。這些機制被稱為代碼度量 (code metric)。
目前一些公司如華為、普元等都在代碼質量方面有比較嚴格的要求,采用CMMI5的規范來評估代碼質量。他們根據單元測試覆蓋率作為代碼質量的一種保證手段。單元測試覆蓋的種類有下面幾種:語句覆蓋、分支覆蓋、條件覆蓋、路徑覆蓋。在單元測試中前三種覆蓋率都非常容易達到,但會存在一定的缺陷。在這篇文章中,我就不詳細解說前三種覆蓋率的計算方法了,重點談一下路徑覆蓋率的問題。
圈復雜度,它可以精確地測量路徑復雜度。通過利用某一方法路由不同的路徑,這一基于整數的度量可適當地描述方法復雜度。實際上,過去幾年的各種研究已經確定:圈復雜度大于 10 的方法存在很大的出錯風險。因為圈復雜度通過某一方法來表示路徑,這是用來確定某一方法到達 100% 的覆蓋率將需要多少測試用例的一個好方法。公式圈復雜度V(G)=P+1 ,P是代碼中判定結點的數量,下面我們看一個簡單的類。
package com.alisoft.kplan.atest;
public class PathTest {
public String testA(boolean p1){
String a = null;
if(p1){
a = “”+ p1+ “”;
}
return a.trim();
}
}
在我們平時開發過程中,通常這樣寫一個測試用例,語句覆蓋率達到100%
package com.alisoft.kplan.atest;
import junit.framework.Assert;
import org.junit.Test;
public
class PathTestTest {
@Test
public void testTestA() {
PathTest pt = new PathTest();
Assert.assertEquals(pt.testA(true), “true”);
}
}
這個測試用例雖然語句覆蓋率達到100%但是我們會發現,其中有一個潛在的空指針錯誤沒有被發現。問題來了,那么我們在編寫測試用例的時候,怎么來寫一個優秀的測試用例呢,答案很簡單,就是根據圈復雜度來計算你的類方法復雜度,圈復雜度值越大,就說明你的方法越復雜,存在的缺陷會越多。通過計算公式V(G)=P+1 testA方法的圈復雜度為2,那么我們只要編寫兩個測試用例就可以就可以完成testA()方法的基本路徑覆蓋。我們在看一下這個測試用例
public class PathTestTest {
@Test
public void testTestA() {
PathTest pt = new PathTest();
Assert.assertEquals(pt.testA(true), “true”);
}
@Test
public void testTestAfalse() {
PathTest pt = new PathTest();
Assert.assertEquals(pt.testA(true), “false”);
}
}
通過這個測試用例,我們就可以很容易的發現方法中的那個空指針錯誤。從這個例子來看,這個方法非常簡單,因為它的圈復雜度只有2 ,像我們有些系統中某些方法的圈復雜度值高達150左右,那么你還能這么容易的發現你的程序缺陷嗎?按理論值來算的話,你需要編寫150個測試用例才能完成每個基本分支的測試。為什么要TDD模式開發?為什么要求大家都寫單元測試?為什么評估軟件質量要用覆蓋率來評估?歸根結底一句話:降低代碼復雜度才能保證軟件質量。
推薦大家使用圈復雜度計算的工具JavaNCSS,可以生成html報告。PMD等工具都可以評估代碼復雜度。
在持續集成環境中,隨時間變化評估方法的復雜度是很有必要的。如果某一方法的圈復雜度值在不斷增長,那么您有兩個響應選擇:
1、確保相關測試用例的路徑覆蓋率是否覆蓋到方法中所有的路徑。
2、重構方法,降低長期維護風險。
原文轉自:http://www.kuqin.com/software-engineer/20090321/41733.html