一、代碼覆蓋率統計工具的能與不能
能:代碼覆蓋率統計工具能用來發現沒有被測試(單元測試、接口自動化測試、ui自動化測試、手工測試等)覆蓋的代碼。
1、測試中未覆蓋的代碼可能存在風險:通過分析未覆蓋的代碼,反推在測試用例設計、測試腳本設計過程中的疏漏,從中找出隱藏的bug。
2、發現測試死角、冗余代碼、歷史廢棄代碼:可以發現多個測試用例都覆蓋不到的代碼。收集方法覆蓋率,為廢棄的代碼提供依據。
3、度量自動化用例:為自動化(單元、接口、ui)測試用例提供覆蓋率統計情況,完善自動化測試用例。
4、精准回歸:構建代碼調用關系,精准的確定回歸測試范圍,避免全量回歸造成測試資源的浪費。
不能:代碼覆蓋率統計不能完全用來衡量代碼質量
100%覆蓋的代碼並不意味着100%無bug的應用。代碼覆蓋率作為一個指導性指標,可以一定程度上反應測試的完備程度,是軟件質量度量的一種手段。
二、覆蓋率計數器
2.1、行覆蓋
行覆蓋又叫語句覆蓋,就是度量被測代碼中每個可執行語句是否被執行到了。這里說的是“可執行語句”,因此就不會包括像C++的頭文件聲明,代碼注釋,空行,等等。非常好理解,只統計能夠執行的代碼被執行了多少行。需要注意的是,單獨一行的花括號{}也常常被統計進去。語句覆蓋常常被人指責為“最弱的覆蓋”,它只管覆蓋代碼中的執行語句,卻不考慮各種分支的組合等等。
**全部未覆蓋:該行中指令均未執行,紅色標志
**部分覆蓋:該行中部分指令執行,黃色標志
**全覆蓋:該行中所有指令已執行,綠色標志
2.2、類覆蓋
當類中至少有一個方法已執行,則該類被認為已執行。
2.3、方法覆蓋
執行到代碼中的每一個非抽象方法(函數)。
2.4、分支覆蓋
為if和switch語句計算分支覆蓋率。這個指標計算一個方法中的分支總數,並決定已執行和未執行的分支的數量。分支覆蓋率在class文件中缺少debug信息時也可使用。異常處理不在分支覆蓋的統計范圍內。
**全部未覆蓋:所有分支均未執行,紅色標志
**部分覆蓋:只有部分分支被執行,黃色標志
**全覆蓋:所有分支均已執行,綠色標志
2.5、指令覆蓋
Java字節碼指令是計數的最小單元,它為執行/未執行代碼提供了大量的信息。這個指標完全獨立於源格式,在類文件中缺少debug信息時也可以使用。
2.6、圈復雜度
在(線性)組合中,計算在一個方法里面所有可能路徑的最小數目。所以復雜度可以作為度量單元測試是否有完全覆蓋所有場景的一個依據。缺失的復雜度同樣表示測試案例沒有完全覆蓋到這個模塊。
三、插樁原理
3.1、On-the-fly插樁
java啟動時添加 -javaagent 參數指定特定的jar文件啟動代理程序,代理程序再通過自定義classloader實現自己的類裝載策略,在類加載之前將探針插入class文件中。
3.2、Offline插裝
在測試前先對文件進行插樁,然后生成插過樁的class或jar包,執行插過樁的class文件或者jar包之后,會生成覆蓋率信息到文件,最后統一對覆蓋率信息進行處理,並生成報告。
3.3、兩種插裝方式對比
On-The-Fly | Offline |
更加方便的獲取代碼覆蓋率,無需提前進行字節碼插樁,可以實時獲取代碼覆蓋率信息 | 適用於以下場景:
|
四、JaCoCo的幾種使用方式
JaCoCo的使用方式有很多,這里指貼出幾種,根據項目的不同可以靈活使用。
4.1 Apache Ant方式
JaCoCo通過配置ant的build.xml,以啟動具有執行記錄的Java程序,並從記錄的數據創建覆蓋率報告。參見:http://eclemma.org/jacoco/trunk/doc/ant.html
主要有以下幾種:Task coverage、Task agent、Task dump、Task merge、Task report、Task instrument
4.2、命令行方式
通過java命令行使用javaagent收集執行信息並根據請求或在JVM退出時將其轉儲。參見 http://www.eclemma.org/jacoco/trunk/doc/agent.html
有三種不同的執行數據輸出模式:
- 文件系統:在JVM終止時,執行數據被寫入本地文件。
- TCP套接字服務器:外部工具可以連接到JVM,並通過套接字連接檢索執行數據。可以在VM退出時進行可選的執行數據重置和執行數據轉儲。
- TCP套接字客戶端:啟動時,JaCoCo代理連接到給定的TCP端點。執行數據根據請求寫入套接字連接。可以在VM退出時進行可選的執行數據重置和執行數據轉儲。
使用方式說明:
-javaagent:[yourpath/]jacocoagent.jar=[option1]=[value1],[option2]=[value2]
舉例:
JAVA_OPTS="-server -Xms2000m -Xmx2000m -Xmn800m -XX:PermSize=64m -XX:MaxPermSize=256m -XX:SurvivorRatio=4 -XX:+UseConcMarkSweepGC -XX:MaxTenuringThreshold=15 -Dfile.encoding=utf8 -Duser.language=zh -javaagent:/home/auser/myproject/apache-tomcat-6.0.37/lib/ jacocoagent.jar=includes=com.xxx.*,output=tcpserver,port=8494, address=10.10.10.10"
4.3、 Eclipse EclDmma Plugin方式
(1) 在Eclipse菜單中選擇Help → Install New Software...
(2) 在安裝彈框中輸入http://update.eclemma.org/,勾選出現的版本。
(3) 核對版本,點擊Next。
(4) 根據向導完成安裝。
(5) 之后就是使用了。
4.4、與Jekins集成
(1) 先要在jenkins上安裝JaCoCo的插件,安裝完成之后在job的配置項中可以增加這個選項如下圖
(2) 選擇后如下圖
第一個錄入框是你的覆蓋率文件(exec),第二個是class文件目錄,第三個是源代碼文件目錄。
(3) 配置好了之后進行構建,構建完成之后job首頁就會出現覆蓋率的趨勢圖,鼠標點擊趨勢圖可以看到覆蓋率詳情,包括具體覆蓋率數據和源碼的覆蓋率情況:
趨勢圖
4.5 Apache Maven方式
參見 http://www.eclemma.org/jacoco/trunk/doc/maven.html
這種方式適合Maven的項目。
下面簡單說下調用方式原理:
就拿官方的Offline Example來說吧,其部分內容如下:

注意藍色的部分,上面的配置主要做了以下幾個事情:
(1) 項目已jar包方式打包,引入junit和jacoco。
(2) Build時執行instrument、report、check。
(3) 覆蓋率生成到target/jacoco.exec
我們看看他是怎么觸發調用的。
在jacoco源碼中:jacoco-maven-plugin\target\classes\META-INF\maven\org.jacoco\jacoco-maven-plugin目錄下有個plugin-help.xml文件,它里面標明了具體的調用方式。
截出instrument這段,關鍵地方就是下面藍色部分。

官網上關於參數的說明:

給出一個整理后的表格:

再給一個jacoco的maven部分的代碼目錄:

到這里,大家應該清楚其調用的方式了吧。
4.6、spring boot項目集成jacoco
1、生成一個spring boot項目,不知道可以百度有的是
2、將demo項目打包成jar包,運行jar包
java -jar demo-0.0.1-SNAPSHOT.jar
3、訪問jacoco官網下載並解壓最新包。
http://www.eclemma.org/jacoco/
4、下載ant並配置:https://ant.apache.org/bindownload.cgi(ant需配合環境變量)
下載解壓縮並配置環境變量
5、進入ant/bin目錄,新建build.xml文件
<?xml version="1.0" encoding="UTF-8"?> <project name="test" xmlns:jacoco="antlib:org.jacoco.ant" > <!--Jacoco的安裝路徑--> <property name="jacocoantPath" value="D:\jacoco-0.8.3\lib\jacocoant.jar"/> <!--最終生成.exec文件的路徑,Jacoco就是根據這個文件生成最終的報告的--> <property name="jacocoexecPath" value="D:\jacoco-0.8.3\target\jacoco.exec"/> <!--生成覆蓋率報告的路徑--> <property name="reportfolderPath" value="D:\jacoco-0.8.3\report"/> <!--遠程tomcat服務的ip地址--> <property name="server_ip" value="127.0.0.1"/> <!--前面配置的遠程tomcat服務打開的端口,要跟上面配置的一樣--> <property name="server_port" value="6300"/> <!--源代碼路徑可以包含多個源代碼--> <property name="webSrcpath" value="D:\springdemo\src\main\java" /> <!--.class文件路徑可以包含多個--> <property name="webClasspath" value="D:\springdemo\target\classes"/> <!--讓ant知道去哪兒找Jacoco--> <taskdef uri="antlib:org.jacoco.ant" resource="org/jacoco/ant/antlib.xml"> <classpath path="${jacocoantPath}" /> </taskdef> <!--dump任務: 根據前面配置的ip地址,和端口號, 訪問目標tomcat服務,並生成.exec文件。--> <target name="dump"> <jacoco:dump address="${server_ip}" reset="true" destfile="${jacocoexecPath}" port="${server_port}" append="false"/> </target> <!--jacoco任務: 根據前面配置的源代碼路徑和.class文件路徑, 根據dump后,生成的.exec文件,生成最終的html覆蓋率報告。--> <target name="report"> <delete dir="${reportfolderPath}" /> <mkdir dir="${reportfolderPath}" /> <jacoco:report> <executiondata> <file file="${jacocoexecPath}" /> </executiondata> <structure name="JaCoCo Report"> <group name="Launch related"> <!--此處配置classes文件地址 --> <classfiles> <fileset dir="${webClasspath}" /> </classfiles> <!--此處配置源碼地址--> <sourcefiles encoding="gbk"> <fileset dir="${webSrcpath}" /> </sourcefiles> </group> </structure> <html destdir="${reportfolderPath}" encoding="utf-8" /> </jacoco:report> </target> </project>
注意:其中幾個重要配置
jacocoexecPath:Jacoco的安裝路徑,這個很好理解就是Jacoco的包解壓縮的位置,注意文檔寫的是window路徑如果linux或mac相應修改
jacocoexecPath:最終生成.exec文件的路徑,之后Jacoco就是根據這個文件生成最終的報告
reportfolderPath:生成報告的路徑,html格式報告
server_ip:遠程tomcat服務的ip地址或spring boot啟動的服務器地址
server_port:服務器端口,跟啟動tomcat或jar時的端口號相同即可
webSrcpath:源代碼路徑,這里就是demo的java文件地址
webClasspath:class文件路徑,這里就是demo編譯后的classes目錄
6、啟動spring boot jar包
java -javaagent:/Users/lrs/jacoco-0.8.5/lib/jacocoagent.jar=includes=*,output=tcpserver,port=6300,address=127.0.0.1 -jar /Users/lrs/jacocoDemo/target/demo-0.0.1-SNAPSHOT.jar
7、執行相應的手工測試用例
8、dump信息
cd /Users/lrs/apache-ant-1.9.15/bin ant dump
9、生成報告
cd /Users/lrs/apache-ant-1.9.15/bin ant report
10、查看報告內容
進入目錄 /Users/lrs/jacoco-0.8.5/report 打開index.html,如下圖:Missed是未覆蓋的數量
注:如果出現亂碼可能因為build.xml配置文件中字符集設置有問題,檢查 sourcefiles encoding 和 destdir="${reportfolderPath}" encoding
五、覆蓋率使用-簡單介紹
5.1、產品提出需求
1、實現一個登錄,輸入用戶名、密碼,用戶名密碼匹配后登錄系統,並顯示用戶信息;
2、用戶名、密碼不符登錄失敗給出錯誤提示“登錄失敗,用戶名或密碼錯誤!!!”;
3、用戶名或密碼必填;
5.2、測試設計用例
1、輸入正確的用戶名、密碼登錄系統,並顯示用戶信息;
2、用戶輸入錯誤的用戶名密碼;
~~~嗯嗯,用例完美了。怎么評判用例好壞呢,啟動項目
5.3、啟動項目
5.4、執行case,偷個懶直接寫好了
第一個case:http://127.0.0.1:8080/login?name=lrs&passwd=code
第二個case:http://127.0.0.1:8080/login?name=lrs1&passwd=code
5.5、生成dump信息
5.6、生成報告
5.7、查看報告
看到有三個分支沒有覆蓋到,兩個是非空判斷,一個是if判斷
5.8、補充設計用例
1、輸入正確的用戶名、密碼登錄系統,並顯示用戶信息;
2、用戶輸入錯誤的用戶名密碼;
3、輸入用戶名為空,提示用戶名必填
4、輸入密碼為空,提示密碼必填
5、輸入用戶名包含大小寫
6、輸入密碼包含大小寫
5.9、重新執行測試
第三個case:http://127.0.0.1:8080/login?name=&passwd=code
注:假設一次沒有補充完整用例,可以通過合並報告顯示到一起(https://www.cnblogs.com/wozijisun/p/10442124.html)
5.10、最終版用例
第一個case:http://127.0.0.1:8080/login?name=lrs&passwd=code
第二個case:http://127.0.0.1:8080/login?name=lrs1&passwd=code
第三個case:http://127.0.0.1:8080/login?passwd=code
第四個case:http://127.0.0.1:8080/login?name=lrs
第五個case:http://127.0.0.1:8080/login?name=lRs&passwd=code
第六個case:http://127.0.0.1:8080/login?name=lrs&passwd=cOde
5.11、重新生成報告
注:遺留兩個問題
1、log沒有執行,原因是我沒有重啟服務器,重啟服務后就會變綠
2、此句 if (name.equalsIgnoreCase("lrs") && passwd.equalsIgnoreCase("code")) 有分支沒有覆蓋到,原因是上邊不允許用戶名或密碼為空。如果想看分支全覆蓋那么改成或即可
我就不一一演示了,感興趣可以自己試下~~~
參考文檔:
https://www.bbsmax.com/A/Vx5MjPX7dN/
https://blog.csdn.net/oqqJohn1234567890/article/details/86648092
https://blog.csdn.net/fjl19900121/article/details/80984632
https://blog.csdn.net/hualusiyu/article/details/80759750
https://blog.csdn.net/TMQ1225/article/details/52221187
https://www.cnblogs.com/wang1001/p/12599202.html
https://www.jianshu.com/p/a955d274dc9b
https://cloud.tencent.com/developer/article/1038055
https://cloud.tencent.com/developer/article/1038149