引言
經(jīng)常有人問(wèn)這樣的問(wèn)題:“我們?cè)谧鰡卧獪y(cè)試郑趁,那測(cè)試覆蓋率要到多少才行梭纹?”绍载。答案其實(shí)很簡(jiǎn)答,“作為指標(biāo)的測(cè)試覆蓋率都是沒(méi)有用處的〖胙鳎”
Martin Fowler(重構(gòu)那本書(shū)的作者)曾經(jīng)寫(xiě)過(guò)一篇博客來(lái)討論這個(gè)問(wèn)題弄企,他指出:把測(cè)試覆蓋作為質(zhì)量目標(biāo)沒(méi)有任何意義约素,而我們應(yīng)該把它作為一種發(fā)現(xiàn)未被測(cè)試覆蓋的代碼的手段。
代碼覆蓋率的意義
- 分析未覆蓋部分的代碼疾呻,從而反推在前期測(cè)試設(shè)計(jì)是否充分蟆肆,沒(méi)有覆蓋到的代碼是否是測(cè)試設(shè)計(jì)的盲點(diǎn)淤齐,為什么沒(méi)有考慮到义锥?需求/設(shè)計(jì)不夠清晰膨更,測(cè)試設(shè)計(jì)的理解有誤敞贡,工程方法應(yīng)用后的造成的策略性放棄等等鹏漆,之后進(jìn)行補(bǔ)充測(cè)試用例設(shè)計(jì)。
- 檢測(cè)出程序中的廢代碼饭聚,可以逆向反推在代碼設(shè)計(jì)中思維混亂點(diǎn)慨蓝,提醒設(shè)計(jì)/開(kāi)發(fā)人員理清代碼邏輯關(guān)系婆跑,提升代碼質(zhì)量数冬。
- 代碼覆蓋率高不能說(shuō)明代碼質(zhì)量高,但是反過(guò)來(lái)看,代碼覆蓋率低挟秤,代碼質(zhì)量不會(huì)高到哪里去抄伍,可以作為測(cè)試自我審視的重要工具之一艘刚。
代碼覆蓋率工具
目前Java常用覆蓋率工具Jacoco、Emma和Cobertura
覆蓋率工具工作流程
1. 對(duì)Java字節(jié)碼進(jìn)行插樁截珍,On-The-Fly和Offine兩種方式攀甚。
2. 執(zhí)行測(cè)試用例,收集程序執(zhí)行軌跡信息岗喉,將其dump到內(nèi)存秋度。
3. 數(shù)據(jù)處理器結(jié)合程序執(zhí)行軌跡信息和代碼結(jié)構(gòu)信息分析生成代碼覆蓋率報(bào)告。
4. 將代碼覆蓋率報(bào)告圖形化展示出來(lái)钱床,如html荚斯、xml等文件格式。
插樁原理
主流代碼覆蓋率工具都采用字節(jié)碼插樁模式,通過(guò)鉤子的方式來(lái)記錄代碼執(zhí)行軌跡信息事期。其中字節(jié)碼插樁又分為兩種模式On-The-Fly和Offine滥壕。On-The-Fly模式優(yōu)點(diǎn)在于無(wú)需修改源代碼,可以在系統(tǒng)不停機(jī)的情況下兽泣,實(shí)時(shí)收集代碼覆蓋率信息绎橘。Offine模式優(yōu)點(diǎn)在于系統(tǒng)啟動(dòng)不需要額外開(kāi)啟代理,但是只能在系統(tǒng)停機(jī)的情況下才能獲取代碼覆蓋率撞叨。 基于以上特性金踪,同時(shí)由于公司使用JDK8,我們采用Jacoco來(lái)獲取集成測(cè)試代碼覆蓋率牵敷,單元測(cè)試使用Cobertura胡岔。
On-The-Fly插樁 Java Agent
- JVM中通過(guò)-javaagent參數(shù)指定特定的jar文件啟動(dòng)Instrumentation的代理程序
- 代理程序在每裝載一個(gè)class文件前判斷是否已經(jīng)轉(zhuǎn)換修改了該文件,如果沒(méi)有則需要將探針插入class文件中枷餐。
- 代碼覆蓋率就可以在JVM執(zhí)行代碼的時(shí)候?qū)崟r(shí)獲取靶瘸。
- 典型代表:Jacoco
On-The-Fly插樁 Class Loader
- 自定義classloader實(shí)現(xiàn)自己的類裝載策略,在類加載之前將探針插入class文件中
- 典型代表:Emma
Offine插樁
- 在測(cè)試之前先對(duì)文件進(jìn)行插樁毛肋,生成插過(guò)樁的class文件或者jar包怨咪,執(zhí)行插過(guò)樁的class文件或者jar包之后,會(huì)生成覆蓋率信息到文件润匙,最后統(tǒng)一對(duì)覆蓋率信息進(jìn)行處理诗眨,并生成報(bào)告。
- Offline插樁又分為兩種:
- Replace:修改字節(jié)碼生成新的class文件
- Inject:在原有字節(jié)碼文件上進(jìn)行修改
- 典型代表:Cobertura
On-The-Fly和Offine比較
- On-The-Fly模式更加方便的獲取代碼覆蓋率孕讳,無(wú)需提前進(jìn)行字節(jié)碼插樁匠楚,可以實(shí)時(shí)獲取代碼覆蓋率信息
- Offline模式適用于以下場(chǎng)景:
- 運(yùn)行環(huán)境不支持java agent
- 部署環(huán)境不允許設(shè)置JVM參數(shù)
- 字節(jié)碼需要被轉(zhuǎn)換成其他虛擬機(jī)字節(jié)碼,如Android Dalvik VM
- 動(dòng)態(tài)修改字節(jié)碼過(guò)程中和其他agent沖突
- 無(wú)法自定義用戶加載類
實(shí)踐應(yīng)用
單元測(cè)試覆蓋率
目前有贊開(kāi)發(fā)人員會(huì)寫(xiě)單元測(cè)試用例厂财,為了能夠引入持續(xù)集成芋簿,我們選取了Sonar+Cobertura來(lái)獲取單元測(cè)試覆蓋率。 我們將代碼覆蓋率綁定到代碼編譯階段璃饱,這樣每次代碼編譯就能夠執(zhí)行單元測(cè)試同時(shí)獲取代碼單元測(cè)試覆蓋率
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>cobertura-maven-plugin</artifactId>
<version>2.7</version>
<configuration>
<formats>
<format>xml</format>
</formats>
</configuration>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>cobertura</goal>
</goals>
</execution>
</executions>
</plugin>
生成代碼覆蓋率文件以后与斤,通過(guò)Jenkins SonarQube Scanner或者執(zhí)行mvn sonar:sonar
將該文件上傳至Sonar 服務(wù)器,就可以解析該文件荚恶,生成圖形化的界面
集成測(cè)試覆蓋率
測(cè)試人員執(zhí)行集成測(cè)試測(cè)試用例時(shí)(包括手工執(zhí)行和自動(dòng)化執(zhí)行)撩穿,我們需要代碼覆蓋率來(lái)發(fā)現(xiàn)測(cè)試用例設(shè)計(jì)的遺漏,及時(shí)補(bǔ)充用例來(lái)覆蓋未被覆蓋到的代碼谒撼。
被測(cè)系統(tǒng)冗锁,在服務(wù)啟動(dòng)時(shí),都會(huì)通過(guò)javaagent的方式做On-The-Fly插樁
被測(cè)服務(wù)器啟動(dòng)之后嗤栓,測(cè)試人員手工執(zhí)行測(cè)試用例冻河,Jacoco Agent會(huì)實(shí)時(shí)將代碼覆蓋率信息傳輸給Jacoco Prase Server箍邮,該服務(wù)器保存了被測(cè)代碼源文件以及編譯后的目標(biāo)文件,服務(wù)器會(huì)結(jié)合源文件叨叙、目標(biāo)文件以及代碼覆蓋率信息生成圖表化的覆蓋率文件锭弊。
-
自動(dòng)化執(zhí)行測(cè)試用例完成之后,獲取代碼覆蓋率信息擂错,通過(guò)Jenkins Jacoco插件解析味滞,獲取圖表化的覆蓋率文件。image
獲取代碼覆蓋率報(bào)告之后钮呀,結(jié)合git獲取的本次代碼變動(dòng)信息剑鞍,得到測(cè)試用例覆蓋的變動(dòng)文件的測(cè)試覆蓋率統(tǒng)計(jì)信息。來(lái)分析是否有由于測(cè)試用例設(shè)計(jì)遺漏導(dǎo)致的代碼沒(méi)有覆蓋或者是開(kāi)發(fā)的無(wú)效代碼導(dǎo)致該代碼無(wú)法被覆蓋爽醋,如果測(cè)試用例設(shè)計(jì)有所遺漏蚁署,可以對(duì)照的增加相應(yīng)的用例;如果是無(wú)效代碼可以刪除蚂四。
自動(dòng)化集成流程
1. 業(yè)務(wù)開(kāi)發(fā)完成之后光戈,開(kāi)發(fā)人員做單元測(cè)試,單元測(cè)試完成之后遂赠,保證單元測(cè)試全部通過(guò)同時(shí)單元測(cè)試代碼覆蓋率達(dá)到一定程度(這個(gè)需要開(kāi)發(fā)和測(cè)試約定久妆,理論上越高越好),開(kāi)發(fā)提測(cè)跷睦。
2. 測(cè)試人員根據(jù)測(cè)試用例進(jìn)行測(cè)試(包括手工測(cè)試和自動(dòng)化測(cè)試)筷弦,結(jié)合git獲取本次變動(dòng)代碼的覆蓋率信息。行覆蓋率需達(dá)到100%抑诸,分支達(dá)到50%以上烂琴,這個(gè)需要具體場(chǎng)景具體分析。
3. 測(cè)試通過(guò)之后哼鬓,代碼合并至主干,進(jìn)行自動(dòng)化回歸边灭。
4. 回歸測(cè)試通過(guò)之后异希,代碼可以上線。
基于這套流程绒瘦,我們可以將單元測(cè)試代碼覆蓋率和集成測(cè)試代碼覆蓋率整合到持續(xù)集成流程中称簿,如果代碼覆蓋率達(dá)不到我們?cè)O(shè)置的某個(gè)值時(shí),可以終止流程繼續(xù)下去獲取需要人工確認(rèn)之后惰帽,繼續(xù)流程憨降。
總結(jié)
本文主要介紹了Java代碼覆蓋率統(tǒng)計(jì)原理以及結(jié)合有贊測(cè)試的工程實(shí)踐介紹了代碼覆蓋率該如何應(yīng)用的實(shí)際測(cè)試中。不管是白盒測(cè)試還是黑盒測(cè)試该酗,代碼覆蓋率統(tǒng)計(jì)都是必不可少的一環(huán)授药,它可以直接反映本次測(cè)試的遺漏點(diǎn)(不是100%反映)士嚎。結(jié)合到自動(dòng)發(fā)布場(chǎng)景也是一個(gè)較好地衡量指標(biāo)。
最后再重申一下本文開(kāi)篇的觀點(diǎn):
- 代碼覆蓋率統(tǒng)計(jì)是用來(lái)發(fā)現(xiàn)沒(méi)有被測(cè)試覆蓋的代碼
- 代碼覆蓋率統(tǒng)計(jì)不能完全用來(lái)衡量代碼質(zhì)量