關(guān)于 gitlab 上 Java 項(xiàng)目測試覆蓋率

gitlab 中是支持將項(xiàng)目的測試覆蓋率解析的, 簡單來說就是通過設(shè)置正則表達(dá)式從 CI 中的日志抓出覆蓋率的相關(guān)信息(參見官方文檔), 我雖然覺得這個(gè)方式挺 LOW 的, 但也沒有什么其他辦法. 記錄一下 Java 項(xiàng)目的接入過程.

0x01 Java 測試覆蓋率統(tǒng)計(jì)

Java 是有一些第三方工具進(jìn)行測試覆蓋率統(tǒng)計(jì)的, 原理也很簡單:

  1. 使用 Java Instrumentation 技術(shù)將字節(jié)碼改寫, 添加一些標(biāo)記代碼, 以備在代碼真正被執(zhí)行的時(shí)候標(biāo)記哪些邏輯/哪些分支被執(zhí)行
  2. 執(zhí)行 Unit Test 代碼
  3. 收集標(biāo)記代碼產(chǎn)生的數(shù)據(jù)并統(tǒng)計(jì)覆蓋率, 生成測試覆蓋率報(bào)告

可選方案也有一些, 羅列幾個(gè)我熟悉的:

  1. Clover, Atlassian 出品, 收費(fèi)
  2. Cobertura, 開源, 最后更新是2015年(懷疑已經(jīng)沒人維護(hù))
  3. Jacoco, 開源, 并且項(xiàng)目依舊活躍

選型原則也很簡單, 一看是否有自己所用的 build 工具集成, 畢竟自己再去寫一個(gè) build 工具 plugin 也費(fèi)力氣; 二看結(jié)果是否滿足需求.

由于 Cobertura 不怎么更新了, 我這個(gè) Cobertura 老用戶也切換到了 Jacoco.

0x02 接入 gitlab CI

接入也很容易, 由于使用 maven 作為 build 工具, 因此只需要在 pom.xml 中添加如下內(nèi)容, 就可以在 mvn clean test 執(zhí)行后自動(dòng)獲得覆蓋率統(tǒng)計(jì)報(bào)告, 在 target/site/jacoco/index.html

<build>
    <plugins>
      <plugin>
         <groupId>org.jacoco</groupId>
         <artifactId>jacoco-maven-plugin</artifactId>
         <version>0.7.9</version>
         <executions>
            <execution>
               <id>pre-unit-test</id>
               <goals>
                  <goal>prepare-agent</goal>
               </goals>
            </execution>
            <execution>
               <id>post-unit-test</id>
               <phase>test</phase>
               <goals>
                  <goal>report</goal>
               </goals>
            </execution>
         </executions>
      </plugin>
    </plugins>
</build>

同樣, 接入 gitlab CI 也是簡單, 由于覆蓋率統(tǒng)計(jì)結(jié)果在 html 報(bào)告中, 因此在項(xiàng)目的 .gitlab-ci.yml 中執(zhí)行 mvn clean test && cat target/site/jacoco/index.html 即可將覆蓋率信息打印到 LOG 中, 最終 gitlab 會(huì)通過 coverage 的正則解析到覆蓋率. 正則也很簡單:

Total.*?([0-9]{1,3})%
將上述正則填寫到項(xiàng)目 Pipeline settings 中

0x02 采坑開始

用上訴方法接入了多個(gè)項(xiàng)目都沒有問題, 遇到一個(gè) Presto plugin 的項(xiàng)目就詭異的報(bào)錯(cuò)了, 現(xiàn)象就是:

  1. 沒有 jacoco-maven-plugin 的情況下執(zhí)行 mvn clean test 測試成功執(zhí)行
  2. 添加了 jacoco-maven-plugin 就會(huì)報(bào)如下錯(cuò)誤:
testValue(data.Test)  Time elapsed: 0.006 sec  <<< ERROR!
java.lang.NoClassDefFoundError: Could not initialize class com.facebook.presto.spi.block.BlockBuilderStatus
    at data.Test.testValue(Test.java:40)
    sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
    at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:252)
    at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:141)
    at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:112)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.apache.maven.surefire.util.ReflectionUtils.invokeMethodWithArray(ReflectionUtils.java:189)
    at org.apache.maven.surefire.booter.ProviderFactory$ProviderProxy.invoke(ProviderFactory.java:165)
    at org.apache.maven.surefire.booter.ProviderFactory.invokeProvider(ProviderFactory.java:85)
    at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:115)
    at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:75)

不過作為 Java 老司機(jī), 看到 java.lang.NoClassDefFoundError: Could not initialize class com.facebook.presto.spi.block.BlockBuilderStatus 就知道是 com.facebook.presto.spi.block.BlockBuilderStatus 這個(gè)類的什么靜態(tài)方法執(zhí)行失敗, 導(dǎo)致 com.facebook.presto.spi.block.BlockBuilderStatus 初始化不成功.

翻了一下日志果然發(fā)現(xiàn)如下錯(cuò)誤:

Caused by: java.lang.IllegalArgumentException: Cannot determine size of boolean[] because it contains an array
    at com.facebook.presto.spi.block.BlockBuilderStatus.deepInstanceSize(BlockBuilderStatus.java:77)
    at com.facebook.presto.spi.block.BlockBuilderStatus.deepInstanceSize(BlockBuilderStatus.java:92)
    at com.facebook.presto.spi.block.BlockBuilderStatus.deepInstanceSize(BlockBuilderStatus.java:92)
    at com.facebook.presto.spi.block.BlockBuilderStatus.<clinit>(BlockBuilderStatus.java:26)
    ... 33 more

通過閱讀源碼發(fā)現(xiàn) com.facebook.presto.spi.block.BlockBuilderStatus 根本就沒有 boolean[] 類型的屬性, 猜測是 jacoco 在改寫字節(jié)碼的時(shí)候添加了一些屬性, 用于標(biāo)記代碼是否被執(zhí)行. 通過 jacoco-maven-plugin 中添加 exclude 排除所有 preseto 相關(guān)代碼, 問題解決.

<configuration>
    <excludes>
        <exclude>com/facebook/**/*</exclude>
    </excludes>
</configuration>

總結(jié)

gitlab 接入一下測試覆蓋率還是很有必要的, gitlab 還支持在項(xiàng)目的 README.md 中添加覆蓋率和 build status 的 badge, 從側(cè)面敦促工程師盡量提升測試覆蓋率. 反正我看到低于 80% 覆蓋率的項(xiàng)目都是嗤之以鼻的.

覆蓋率 badge

還有一個(gè)收獲就是先了解原理, 在動(dòng)手干活兒很重要, 遇到問題從原理角度定位問題比盲目的瞎嘗試效率高得多.

-- EOF --

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末恤磷,一起剝皮案震驚了整個(gè)濱河市米丘,隨后出現(xiàn)的幾起案子鸯乃,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,548評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡畸肆,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,497評論 3 399
  • 文/潘曉璐 我一進(jìn)店門宙址,熙熙樓的掌柜王于貴愁眉苦臉地迎上來轴脐,“玉大人,你說我怎么就攤上這事』砘裕” “怎么了令野?”我有些...
    開封第一講書人閱讀 167,990評論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長徽级。 經(jīng)常有香客問我气破,道長,這世上最難降的妖魔是什么餐抢? 我笑而不...
    開封第一講書人閱讀 59,618評論 1 296
  • 正文 為了忘掉前任现使,我火速辦了婚禮,結(jié)果婚禮上旷痕,老公的妹妹穿的比我還像新娘碳锈。我一直安慰自己,他們只是感情好欺抗,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,618評論 6 397
  • 文/花漫 我一把揭開白布售碳。 她就那樣靜靜地躺著,像睡著了一般绞呈。 火紅的嫁衣襯著肌膚如雪贸人。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,246評論 1 308
  • 那天佃声,我揣著相機(jī)與錄音艺智,去河邊找鬼。 笑死圾亏,一個(gè)胖子當(dāng)著我的面吹牛十拣,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播志鹃,決...
    沈念sama閱讀 40,819評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼夭问,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了曹铃?” 一聲冷哼從身側(cè)響起甲喝,我...
    開封第一講書人閱讀 39,725評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎铛只,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體糠溜,經(jīng)...
    沈念sama閱讀 46,268評論 1 320
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡淳玩,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,356評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了非竿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片蜕着。...
    茶點(diǎn)故事閱讀 40,488評論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出承匣,到底是詐尸還是另有隱情蓖乘,我是刑警寧澤,帶...
    沈念sama閱讀 36,181評論 5 350
  • 正文 年R本政府宣布韧骗,位于F島的核電站嘉抒,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏袍暴。R本人自食惡果不足惜些侍,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,862評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望政模。 院中可真熱鬧岗宣,春花似錦、人聲如沸淋样。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,331評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽趁猴。三九已至刊咳,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間躲叼,已是汗流浹背芦缰。 一陣腳步聲響...
    開封第一講書人閱讀 33,445評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留枫慷,地道東北人让蕾。 一個(gè)月前我還...
    沈念sama閱讀 48,897評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像或听,于是被迫代替她去往敵國和親探孝。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,500評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理誉裆,服務(wù)發(fā)現(xiàn)顿颅,斷路器,智...
    卡卡羅2017閱讀 134,696評論 18 139
  • 軟件的持續(xù)集成工具之一足丢,易上手粱腻,功能強(qiáng)大,話不多說斩跌,干貨奉上绍些。我的博客地址:http://blog.lzoro.c...
    格子Lin閱讀 12,236評論 10 41
  • 我很想你
    章欣宜閱讀 92評論 0 0
  • 偶爾從網(wǎng)上找些圖片柬批,自己沒事畫著玩啸澡! 因?yàn)榘l(fā)現(xiàn)的簡書,我更喜歡分享氮帐,雖然我并不聰明也不勤快嗅虏,但值得開心的我一直沒有丟棄
    半暖時(shí)光丫閱讀 343評論 5 2
  • 來源:互聯(lián)網(wǎng)自己整理發(fā)布給大家 簡歷模板莫名其妙就是面試用的,分享給那些還在找工作的小伙伴上沐,以下是壓縮包目錄因?yàn)樘?..
    小向資源網(wǎng)閱讀 356評論 0 3