前言
在現(xiàn)在的軟件開發(fā)中,由于軟件的復(fù)雜度越來越高,業(yè)務(wù)也覆蓋很廣,各個業(yè)務(wù)模塊業(yè)務(wù)錯綜復(fù)雜。這樣就需要我們需要團隊開發(fā),在我們團隊中開發(fā)人員的經(jīng)驗、代碼風格樣式都不一致,以及缺乏統(tǒng)一的標準,從而導(dǎo)致我們的整個項目的的代碼難以閱讀,不便于后期維護。這幾天在研究代碼質(zhì)量管理,根據(jù)在網(wǎng)上搜集的資料及跟前輩學的一點經(jīng)驗整理一下,有需要的同學們可以查看,也便于以后自己回顧。
主要對下面的七塊進行分析
編碼格式規(guī)范
代碼重復(fù)
代碼覆蓋率
依賴項分析
復(fù)雜度監(jiān)控
JAVA模擬技術(shù)
代碼評審和重構(gòu)
接下來的使用Eclipse 插件來揭示這些分析領(lǐng)域:
編碼格式規(guī)范:codeStyle和CheckStyle
代碼重復(fù):PMD 的 CPD
代碼覆蓋率:Coverlipse或者Emma
依賴項分析:JDepend
復(fù)雜度監(jiān)控:Metrics
Java模擬技術(shù):EasyMock、PowerMock
代碼評審和重構(gòu):Jupiter
編碼格式規(guī)范
codestyle介紹
統(tǒng)一的代碼規(guī)范能提高代碼的可讀性、可維護性。
一般規(guī)則和格式規(guī)范:如代碼縮進、程序塊規(guī)范、每行最大代碼長度;
命名規(guī)范:如包名、類名、接口名、枚舉、屬性名、方法名、參數(shù)名等命名規(guī)則;
文檔規(guī)范:如類文件頭注釋、變量注釋、方法注釋等;
編程規(guī)范:如異常、并發(fā)、多線程等;
其它規(guī)范:如日志格式等。
圖1
可以導(dǎo)入代碼格式,實現(xiàn)統(tǒng)一。
checkstyle介紹
安裝checkstyle 的Eclipse插件
1.下載地址:
http://pan.baidu.com/s/1o6LOSwM
2.解壓
net.sf.eclipsecs-updatesite_5.6.1.201306282206-bin.zip文件,到系統(tǒng)路徑下。如:D:geyouchaoeclipse-pluginscs(注:一定不用起名為checkstyle,不知道為什么此名就是安裝不成功),此文件夾下有兩個文件夾features、plugins。
3.我們使用link的方式安裝。在Eclipse的dropins文件夾下新建checkstyle.link文件,內(nèi)容為:
path=D:\geyouchao\eclipse-plugins\cs
4.關(guān)閉Eclipse,重啟。然后在Eclipse的window》Preferences下就可以看到checkstyle菜單,安裝成功,如下圖
圖2
使用checkstyle
自定義CheckStyle規(guī)則,下面是我定義的CheckStyle模板,然后導(dǎo)入
圖3
把新導(dǎo)入的,設(shè)置為默認
圖4
可以修改其中的值,點擊“Configure…”按鈕。
圖5
下面是我自定義的CheckStyle.xml文件,供參考。
<xml version="1.0"encoding="UTF-8">
<!DOCTYPE module PUBLIC "-//PuppyCrawl//DTD Check Configuration 1.2//EN""http://www.puppycrawl.com /dtds/configuration_1_2.dtd">
<module name="Checker">
<propertyname="severity" value="warning"/>
<property name="charset" value="UTF-8"/>
<!-- 長度方面的檢查 -->
<!-- 文件長度不超過1500行 -->
<module name="FileLength">
<property name="max" value="1500" />
</module>
<module name="TreeWalker">
<!-- javadoc的檢查 -->
<!-- 檢查所有的interface和class -->
<module name="JavadocType" />
<!-- 命名方面的檢查 -->
<!-- 局部的final變量,包括catch中的參數(shù)的檢查 -->
<module name="LocalFinalVariableName" />
<!-- 局部的非final型的變量,包括catch中的參數(shù)的檢查 -->
<module name="LocalVariableName" />
<!-- 包名的檢查(只允許小寫字母) -->
<module name="PackageName">
<property name="format"value="^[a-z]+ (.[a-z][a-z0-9]*)*$" />
</module>
<!--僅僅是static型的變量(不包括static final型 )的檢查 -->
<module name="StaticVariableName" />
<!-- 類型(Class或Interface)名的檢查 -->
<module name="TypeName" />
<!-- 非static型變量的檢查 -->
<module name="MemberName" />
<!-- 方法名的檢查 -->
<module name="MethodName" />
<!-- 方法的參數(shù)名 -->
<modulename="ParameterName " />
<!-- 常量名的檢查 -->
<module name="ConstantName" />
<!-- 沒用的import檢查,比如:1.沒有被用到 2.重復(fù)的 3.import java.lang的 4.import 與該類在同一個package的 -->
<module name="UnusedImports" />
<!-- 每行不超過150個字-->
<module name="LineLength">
<property name="max" value="150" />
</module>
<!-- 方法不超過150行 -->
<module name="MethodLength">
<property name="tokens" value="METHOD_DEF"/>
<property name="max"value="150" />
</module>
<!-- 方法的參數(shù)個數(shù)不超過5個。并且不對構(gòu)造方法進行檢查-->
<module name="ParameterNumber">
<property name="max" value="5" />
<property name="tokens" value="METHOD_DEF"/>
</module>
<!-- 空格檢查 -->
<!-- 允許方法名后緊跟左邊圓括號"(" -->
<module name="MethodParamPad" />
<!-- 在類型轉(zhuǎn)換時,不允許左圓括號右邊有空格,也不允許與右圓括號左邊有空格 -->
<module name="TypecastParenPad" />
<!-- 關(guān)鍵字 -->
<!--
每個關(guān)鍵字都有正確的出現(xiàn)順序。比如 public static final XXX 是對一個常量的聲明。如果使用 static
public final 就是錯誤的
-->
<module name="ModifierOrder" />
<!-- 多余的關(guān)鍵字 -->
<module name="RedundantModifier" />
<!-- 對區(qū)域的檢查 -->
<!-- 不能出現(xiàn)空白區(qū)域 -->
<module name="EmptyBlock" />
<!-- 所有區(qū)域都要使用大括號 -->
<module name="NeedBraces" />
<!-- 多余的括號 -->
<module name="AvoidNestedBlocks">
<property name="allowInSwitchCase" value="true"/>
</module>
<!-- 編碼方面的檢查 -->
<!-- 不許出現(xiàn)空語句 -->
<module name="EmptyStatement" />
<!-- 不允許魔法數(shù) -->
<module name="MagicNumber">
<property name="tokens"value="NUM_DOUBLE, NUM_INT" />
</module>
<!-- 多余的throw -->
<module name="RedundantThrows" />
<!-- String的比較不能用!= 和 == -->
<module name="StringLiteralEquality" />
<!-- if最多嵌套3層 -->
<module name="NestedIfDepth">
<property name="max" value="3" />
</module>
<!-- try最多被嵌套2層 -->
<module name="NestedTryDepth">
<property name="max" value="2" />
</module>
<!-- clone方法必須調(diào)用了super.clone() -->
<module name="SuperClone" />
<!-- finalize 必須調(diào)用了super.finalize() -->
<module name="SuperFinalize" />
<!-- 不能catch java.lang.Exception -->
<module name="IllegalCatch">
<property name="illegalClassNames"value="java.lang.Exception" />
</module>
<!-- 確保一個類有package聲明 -->
<module name="PackageDeclaration" />
<!-- 一個方法中最多有3個return -->
<modulename="ReturnCount">
<property name="max" value="3" />
<property name="format" value="^$" />
</module>
<!--
根據(jù) Sun 編碼規(guī)范, class 或 interface 中的順序如下: 1.class 聲明。首先是 public,
然后是protected , 然后是 package level (不包括access modifier )最后是private .
(多個class放在一個java文件中的情況) 2.變量聲明。首先是 public, 然后是protected然后是 package
level (不包括access modifier )最后是private . (多個class放在一個java文件中的情況)
3.構(gòu)造函數(shù) 4.方法
-->
<module name="DeclarationOrder" />
<!-- 同一行不能有多個聲明 -->
<module name="MultipleVariableDeclarations" />
<!-- 不必要的圓括號 -->
<module name="UnnecessaryParentheses" />
<!-- 檢查并確保所有的常量中的L都是大寫的。因為小寫的字母l跟數(shù)字1太象了 -->
<module name="UpperEll" />
<!-- 檢查數(shù)組類型的定義是String[] args,而不是String args[] -->
<module name="ArrayTypeStyle" />
<!-- 檢查java代碼的縮進默認配置:基本縮進4個空格,新行的大括號:0。新行的case 4個空格
<module name="Indentation" />
</module>
</module>
下面是使用CheckStyle檢查過得代碼
圖6
常見錯誤分析
常見的CheckStyle錯誤有這些:
1.Type is missing a javadoc commentClass
缺少類型說明
2.“{” should be on the previous line
“{” 應(yīng)該位于前一行
3.Methods is missing a javadoc comment
方法前面缺少javadoc注釋
4.Expected@throwstag for “Exception”
在注釋中希望有@throws的說明
5.“.” Is preceeded with whitespace “.”
前面不能有空格
6.“.” Is followed by whitespace“.”
后面不能有空格
7.“=” is not preceeded with whitespace
“=” 前面缺少空格
8.“=” is not followed with whitespace
“=” 后面缺少空格
9.“}” should be on the same line
“}” 應(yīng)該與下條語句位于同一行
10.Unused@paramtag for “unused”
沒有參數(shù)“unused”,不需注釋
11.Variable “CA” missing javadoc
變量“CA”缺少javadoc注釋
12.Line longer than 80characters
行長度超過80
13.Line contains a tab character
行含有”tab” 字符
14.Redundant “Public” modifier
冗余的“public”modifier
15.Final modifier out of order with the JSL
suggestionFinal modifier的順序錯誤
16.Avoid using the “.*” form of import
Import格式避免使用“.*”
17.Redundant import from the same package
從同一個包中Import內(nèi)容
18.Unused import-java.util.list
Import進來的java.util.list沒有被使用
19.Duplicate import to line 13
重復(fù)Import同一個內(nèi)容
20.Import from illegal package
從非法包中 Import內(nèi)容
21.“while” construct must use “{}”
“while” 語句缺少“{}”
22.Variable “sTest1” must be private and haveaccessor method
變量“sTest1”應(yīng)該是private的,并且有調(diào)用它的方法
23.Variable “ABC” must match pattern“^[a-z][a-zA-Z0-9]*$”
變量“ABC”不符合命名規(guī)則“^[a-z][a-zA-Z0-9]*$”
24.“(” is followed by whitespace
“(” 后面不能有空格
25.“)” is proceeded by whitespace
“)” 前面不能有空格
代碼重復(fù)
PMD介紹
安裝PMD 的Eclipse插件
1.下載地址:
http://jingyan.baidu.com/article/1919 2ad835de6ee53e57073c.html
2.解壓
net.sourceforge.pmd.eclipse-3.2.6.v200903300643.zip文件,到系統(tǒng)路徑下。如:D:geyouchaoeclipse-pluginspmd,此文件夾下有兩個文件夾features、plugins。
3.我們使用link的方式安裝。在Eclipse的dropins文件夾下新建pmd.link文件,內(nèi)容為:
path=D:\geyouchao\eclipse-plugins\pmd
4.關(guān)閉Eclipse,重啟。然后在Eclipse的window》Preferences下就可以看到PMD菜單,安裝成功,如下圖
圖7
使用PMD
圖8
下面是PCD生成的重復(fù)代碼,可以對其中的代碼進行分析,修改
圖9
代碼覆蓋率
Coverlipse介紹
安裝coverlipse 的Eclipse插件
1.下載地址:
https://sourceforge.net/projects/coverlipse /files/Coverlipse/
下載下圖中5個文件
圖10
1.解壓coverlipse-0.9.6.zip文件,到系統(tǒng)路徑下。如:D:geyouchaoeclipse-plugins coverlipse-0.9.6,此文件夾下有兩個文件夾features、plugins。
2.我們使用link的方式安裝。在Eclipse的dropins文件夾下新建coverlipse.link文件,內(nèi)容為:
path=D:\geyouchao\eclipse-plugins\coverlipse
3.關(guān)閉Eclipse,重啟。右鍵java代碼,點擊dubug,安裝成功,如下圖
圖11
依賴項分析
jdepend介紹
安裝jdepend 的Eclipse插件
1.下載地址:
http://andrei.gmxhome.de/ jdepend4eclipse/links.html
圖12
2.拷貝
de.loskutov.eclipse.jdepend_1.2.4.201406241900.jar文件,到Eclipse的dropins目錄下。如:D:geyouchaoeclipse4.2-xudropins。
3.關(guān)閉Eclipse,重啟。通過右鍵單擊源文件夾并選擇Run JDepend Analysis。一定要選擇一個含源代碼的源文件夾;否則看不到此菜單項。
圖13
使用jdepend
圖14
下面對jdepend的分析的結(jié)果簡單介紹
圖15
1.Selected objects():選擇分析的包
2.Package:包全路徑
3.CC(concr.cl.):當前行對應(yīng)包的具體類的數(shù)量。
4.AC(abstr.cl.):當前行對應(yīng)包的抽象類和接口的數(shù)量。
5.Ca(aff.):依賴于被分析package的其他package的數(shù)量,用于衡量pacakge的職責。即有多少包調(diào)用了它。(AfferentCouplings)
6.Ce(eff.):被分析package的類所依賴的其他package的數(shù)量,用于衡量package的獨立性。即它調(diào)用了多少其他包。(EfferentCouplings)
7.A:被分析package中的抽象類和接口與所在package所有類數(shù)量的比例,取值范圍為0-1。(Abstractness)
8.I:I=Ce/(Ce+Ca),用于衡量package的不穩(wěn)定性,取值范圍為0-1。I=0表示最穩(wěn)定,I=1表示最不穩(wěn)定。即如果這個類不調(diào)用任何其他包,則它是最穩(wěn)定的。(Instability)
9.D:分析package和理想曲線A+I=1的垂直距離,用于衡量package在穩(wěn)定性和抽象性之間的平衡。(Distance)
理想的package要么完全是抽象類和穩(wěn)定(x=0,y=1),要么完全是具體類和不穩(wěn)定(x=1,y=0)。取值范圍為0-1,
D=0表示完全符合理想標準,
D=1表示package最大程度地偏離了理想標準。即你的包要么全是接口,不調(diào)用任何其他包(完全是抽象類和穩(wěn)定),要么是具體類,不被任何其他包調(diào)用。
10.Cycle!:循環(huán)依賴
11. Package with cycle:包與包直接有循環(huán)調(diào)用
12. Depends
upon-efferentdependencies:依賴的包
13. Used by-afferentdependencies:被引用的包
圖16
Instability:不穩(wěn)定
Abstractness:抽象性
問題分析
圖17
針對上圖中Cycle!列中有感嘆號圖標問題,是因為以上三個包中的類有傳遞依賴,故出現(xiàn)此警告。
解決辦法:
把其中的某個或者某些類再單獨抽出新包,解決此問題。
復(fù)雜度監(jiān)控
metrics(量度)介紹
安裝metrics 的Eclipse插件
1.下載地址:
https://sourceforge.net/projects/metrics/
圖18
圖19
圖20
圖21
2.解壓updatesite_1.3.6.zip文件,
net.sourceforge.metrics.updatesite文件夾下有features和plugins分別拷貝到Eclipse的對應(yīng)目錄下。如:D:geyouchaoeclipse4.2-xuplugins等。
3.關(guān)閉Eclipse,重啟。然后在Eclipse的window》Preferences下就可以看到Metrics Preferences菜單,安裝成功,如下圖
圖22
設(shè)置metrics參數(shù)
下圖是metrics提供的配置項,下面對各個配置項進行解釋
圖23
lack of cohesion of methods:
介于0-1之間,0表示最有凝聚力,1表示完全沒有凝聚力。
1)如果所有方法都使用所有的實例字段,一個類是完全有凝聚力的
2)靜態(tài)方法和實例方法計數(shù),它還包括構(gòu)造函數(shù)、屬性的getter和setter,所有方法。
在Since Sonar 4.1中此量度已經(jīng)被刪除。
3) 子的數(shù)量NOC(Number ofchildren)子類在類的層次內(nèi),子類可以最直接地從屬于一類。隨著子類數(shù)量的增大,重用也增加了。但父類抽象的表示可能減少,即一些子類可能不是父類真正的成員,同時,測試數(shù)量(用來檢查每個子類在操作前后的要求)也將增加。
4) 方法中聚合的不足LCOM(Lack ofcohesion in Methods)
一個類內(nèi)的每種方法訪問一個或多個屬性(也稱實例變量)。LCOM是訪問一個或多個相同屬性方法的數(shù)量
如果LCOM很大,則說明方法可以通過屬性與其他方法耦合,這就增加了類設(shè)計的復(fù)雜性。通常,對LCOM值很大的類,可以把它分為兩個或多個單獨的類,這樣每個類能的設(shè)計更方便。
這里講的耦合和聚合與傳統(tǒng)軟件中講的是一樣的。我們希望高聚合和低耦合,即保持低的LCOM.但在某些時候,LCOM很大也是合理的。
5)每個類的加權(quán)方法WMC(Weighted Methodsper Class)
6)Out-of-range:超出范圍,溢出
下面是metric的安全范圍設(shè)置,在此頁面中可以設(shè)置每項指標的安全范圍。若警告啟用,指標值超出我們設(shè)置的安全范圍就好發(fā)出警告。
圖24
使用metrics
1.右鍵單擊您的項目并選擇 Properties 菜單。在結(jié)果窗口中,選擇 EnableMetrics plugin 復(fù)選框并單擊 OK:
圖25
2.從 Eclipse 中選擇 Window 菜單打開 Metrics 視圖,然后選擇 Show View | Other...。
3.選擇 Metrics | Metrics View 打開如圖 13 中顯示的窗口。您需要使用Java 透視圖并重新構(gòu)建項目,從而顯示這些度量值。
圖26
注:一定要重新構(gòu)建項目
圖27
Java 模擬技術(shù)(mock)
Mock是什么
Mock通常是指,在測試一個對象A時,我們構(gòu)造一些假的對象來模擬與A之間的交互,而這些Mock對象的行為是我們事先設(shè)定且符合預(yù)期。通過這些Mock對象來測試A在正常邏輯,異常邏輯或壓力情況下工作是否正常。
引入Mock最大的優(yōu)勢在于:Mock的行為固定,它確保當你訪問該Mock的某個方法時總是能夠獲得一個沒有任何邏輯的直接就返回的預(yù)期結(jié)果。
Mock Object的使用通常會帶來以下一些好處:
隔絕其他模塊出錯引起本模塊的測試錯誤。
隔絕其他模塊的開發(fā)狀態(tài),只要定義好接口,不用管他們開發(fā)有沒有完成。
一些速度較慢的操作,可以用MockObject代替,快速返回。
對于分布式系統(tǒng)的測試,使用Mock Object會有另外兩項很重要的收益:
通過Mock Object可以將一些分布式測試轉(zhuǎn)化為本地的測試
將Mock用于壓力測試,可以解決測試集群無法模擬線上集群大規(guī)模下的壓力
mock技術(shù)的目的和作用是模擬一些在應(yīng)用中不容易構(gòu)造或者比較復(fù)雜的對象,從而把測試與測試邊界以外的對象隔離開。
Mock應(yīng)用場景
在使用Mock的過程中,發(fā)現(xiàn)Mock是有一些通用性的,對于一些應(yīng)用場景,是非常適合使用Mock的:
真實對象具有不可確定的行為(產(chǎn)生不可預(yù)測的結(jié)果,如股票的行情)
真實對象很難被創(chuàng)建(比如具體的web容器)
真實對象的某些行為很難觸發(fā)(比如網(wǎng)絡(luò)錯誤)
真實情況令程序的運行速度很慢
真實對象有用戶界面
測試需要詢問真實對象它是如何被調(diào)用的(比如測試可能需要驗證某個回調(diào)函數(shù)是否被調(diào)用了)
真實對象實際上并不存在(當需要和其他開發(fā)小組,或者新的硬件系統(tǒng)打交道的時候,這是一個普遍的問題)
當然,也有一些不得不Mock的場景:
一些比較難構(gòu)造的Object:這類Object通常有很多依賴,在單元測試中構(gòu)造出這樣類通常花費的成本太大。
執(zhí)行操作的時間較長Object:有一些Object的操作費時,而被測對象依賴于這一個操作的執(zhí)行結(jié)果,例如大文件寫操作,數(shù)據(jù)的更新等等,出于測試的需求,通常將這類操作進行Mock。
異常邏輯:一些異常的邏輯往往在正常測試中是很難觸發(fā)的,通過Mock可以人為的控制觸發(fā)異常邏輯。
在一些壓力測試的場景下,也不得不使用Mock,例如在分布式系統(tǒng)測試中,通常需要測試一些單點(如namenode,jobtracker)在壓力場景下的工作是否正常。而通常測試集群在正常邏輯下無法提供足夠的壓力(主要原因是受限于機器數(shù)量),這時候就需要應(yīng)用Mock去滿足。
在這些場景下,我們應(yīng)該如何去做Mock的工作了,一些現(xiàn)有的Mock工具可以幫助我們進行Mock工作。
EasyMock應(yīng)用
Easymock官網(wǎng):
http://easymock.org/
EasyMock 是早期比較流行的MocK測試框架。它提供對接口的模擬,能夠通過錄制、回放、檢查三步來完成大體的測試過程,可以驗證方法的調(diào)用種類、次數(shù)、順序,可以令 Mock 對象返回指定的值或拋出指定異常。通過 EasyMock,我們可以方便的構(gòu)造 Mock 對象從而使單元測試順利進行。
EasyMock 是采用 MIT license 的一個開源項目
使用EasyMock大致可以劃分為以下幾個步驟:
① 使用 EasyMock 生成 Mock 對象;
② 錄制 Mock 對象的預(yù)期行為和輸出;
③ 將 Mock 對象切換到 播放 狀態(tài);
④ 調(diào)用 Mock 對象方法進行單元測試;
⑤ 對 Mock 對象的行為進行驗證。
mockito應(yīng)用
mockito官網(wǎng)
https://code.google.com/p/mockito/
由于官網(wǎng)上不了,可以到csdn上下載
http://download.csdn.net/download /wjjiang917/5519381
是EasyMock之后流行的mock工具。相對EasyMock學習成本低,而且具有非常簡潔的API,測試代碼的可讀性很高。
使用mockito大致可以劃分為以下幾個步驟:
① 使用 mockito 生成 Mock 對象;
② 定義(并非錄制) Mock 對象的行為和輸出(expectations部分);
③ 調(diào)用 Mock 對象方法進行單元測試;
④ 對 Mock 對象的行為進行驗證。
PowerMock應(yīng)用
PowerMock官網(wǎng)
https://code.google.com/p/powermock/
這個工具是在EasyMock和Mockito上擴展出來的,目的是為了解決EasyMock和Mockito不能解決的問題,比如對static, final, private方法均不能mock。其實測試架構(gòu)設(shè)計良好的代碼,一般并不需要這些功能,但如果是在已有項目上增加單元測試,老代碼有問題且不能改時,就不得不使用這些功能了。
PowerMock 在擴展功能時完全采用和被擴展的框架相同的API, 熟悉 PowerMock 所支持的模擬框架的開發(fā)者會發(fā)現(xiàn) PowerMock 非常容易上手。PowerMock 的目的就是在當前已經(jīng)被大家所熟悉的接口上通過添加極少的方法和注釋來實現(xiàn)額外的功能。目前PowerMock 僅擴展了 EasyMock 和 mockito,需要和EasyMock或Mockito配合一起使用。
因為內(nèi)容實在是太多了,小編在此就不做過多的介紹了,想了解更多的Java知識可以關(guān)注小編!