GitLab CI - 在 Merge Request(MR) 中檢視 PHP 測試代碼覆蓋 (Code Coverage) 情況

2022.06.30 Update

GitLab CI/CD 手冊上發現 artifacts:reports:cobertura 在 GitLab 15.0 中移除,替換為 artifacts:reports:coverage_report,因此更新底下內容。

2021.06.14 Update

在上一篇 在 Pipeline 檢視 PHPUnit 單元測試報告,透過 artifacts:reports:junit 的機制,使每次 Pipeline 執行後,就能馬上看到單元測試報告,有了測試報告之後,可能就會開始思考,那 Code Coverage 測試代碼覆蓋的狀況,是不是也可以在 Merge Request 的過程中就看到呢?

答案是可以的,而且在 GitLab CI/CD 的免費版本中就有這個功能。在 GitLab 12.9 版後,只需要透過 artifacts:reports:coverage_report 並作相關設定,就可以搜集 Cobertura XML 格式的報表就可以完成。這部分與 artifacts:reports:junit 的原理是一樣的,只需要讓 Pipeline 的工作可以產出所需要的格式的檔案,透過 artifacts:reports:coverage_report 將檔案搜集起來,就可以在 Merge Request 的畫面中看到視覺化的測試代碼覆蓋情況。

Test Coverage Visualization

那麼,在 PHP 該怎麼做出符合Cobertura XML格式的測試程式碼覆蓋狀況檔案呢?

如上圖(取自官方Test coverage visualization文件圖片),在圖片中,呈現綠色線條的部分,代表測試有執行到的部分,而橘色的部分則代表沒有測試程式覆蓋到。

在 GitLab 官方文件上提到了幾個語言及其支援的工具或外掛,如:

在 PHP 搭配 PHPUnit 該如何產出 Cobertura XML 呢?

一、讓 PHPUnit 輸出 Coverage Cobertura XML 格式的報表

在 PHPUnit 9.5 版的手冊中,關於 coverage 的章節中,可以看到,目前手冊上顯示如果要輸出報表,總共支援了底下的六種格式:

  • clover
  • crap4j
  • html
  • php
  • text
  • xml

其中,並沒有提到 GitLab CI/CD 要使用的 Cobertura XML 格式,因此甚至有人寫了轉換工具soyhuce/phpunit-to-cobertura,但仔細探究 PHPUnit 的原始碼,搜尋「cobertura」後發現了一道曙光phpunit/Builder.php at sebastianbergmann/phpunit),在 phpunit 的指令中,支援了 coverage-cobertura 關鍵字。

於是實際驗證,在已經開啟 xdebug 的 PHP 環境中,執行:

1
php vendor/bin/phpunit --coverage-cobertura=cobertura.xml

真的順利產出 Cobertura XML 格式的報表,樣式如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE coverage SYSTEM "http://cobertura.sourceforge.net/xml/coverage-04.dtd">
<coverage line-rate="0.5" branch-rate="0" lines-covered="1" lines-valid="2" branches-covered="0" branches-valid="0" complexity="2" version="0.4" timestamp="1623610530">
<sources>
<source>/Users/mouson/GitFolder/GitLabCI-Playground/GitLab-CodeCoverage-Report-Demo/src</source>
</sources>
<packages>
<package name="" line-rate="0.5" branch-rate="0" complexity="2">
<classes>
<class name="Mouson\Template\Sample" filename="Sample.php" line-rate="0.5" branch-rate="0" complexity="2">
<methods>
<method name="isTrue" signature="" line-rate="0" branch-rate="0" complexity="1">
<lines>
<line number="9" hits="0"/>
</lines>
</method>
<method name="not" signature="bool $bool" line-rate="1" branch-rate="0" complexity="1">
<lines>
<line number="14" hits="1"/>
</lines>
</method>
</methods>
<lines>
<line number="9" hits="0"/>
<line number="14" hits="1"/>
</lines>
</class>
</classes>
</package>
</packages>
</coverage>

這代表至少在 PHPUnit 9.x 的版本中,可以支援 --coverage-cobertura 來產出 Cobertura XML 格式報表。

二、如何在 phpunit.xml 中宣告要產出 Cobertura XML 格式的報表呢?

同樣的,在 PHPUnit 的官方手冊中,是沒辦法找到在 PHPUnit 設定檔中應該如何產出 Cobertura XML 格式報表的,但如同在第一章中驗證的方法,在 coverage 的 report 做了 cobertura 的宣告:

1
2
3
4
5
6
7
8
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./src</directory>
</include>
<report>
<cobertura outputFile="build/cobertura.xml"/>
</report>
</coverage>

同樣產出了 Cobertura XML 格式報表,因此這在 phpunit.xmlphpunit.xml.dist 是可行的。

三、如何在 GitLab CI/CD 中整合,讓PHPUnit 產出 Cobertura XML 報表格式

在這次的範例,直接取用中上一篇在 Pipeline 檢視 PHPUnit 單元測試報告相同的原始碼,但由於 PHPUnit 要產出 Code Coverage 相關的資訊時,必須搭配 xdebug 使用,因此這邊改採 jorge07/alpine-php:8.0-dev docker image 作為執行環境,同樣以 PHP 8.0 作為 PHP 的版本。

1. GitLab CI 執行環境

2. 在 GitLab 共用的模板

1
2
3
4
5
6
7
.test_template:
stage: test
image: "jorge07/alpine-php:8.0-dev"
before_script:
- export XDEBUG_MODE=coverage
- export COMPOSER_ALLOW_XDEBUG=1
- composer install --prefer-dist > /dev/null

在底下的範例中,使用的模板都使用以上的共用安裝過程及共用的 docker image jorge07/alpine-php:8.0-dev。值得一提的地方是,在 PHP 8.0 搭配 XDebug 3.0 後,在取得 Coverage 時,必須讓 XDebug 知道 XDEBUG_MODEcoverage

四、在 GitLab CI 中執行 PHPUnit 的 Coverage 檢查,並產出 Cobertura XML 格式報表

1. 在 PHPUnit Command Line 執行指令中設定 Cobertura XML Report 格式

如前面所提到的,在 Command Line 中要得到 Cobertura XML 格式的報表,必須在 PHPUnit 執行的指令中加上 --coverage-cobertura filePath 參數,在 .gitlab-ci.yml 中,根據 上一篇 的範例原始碼,script 的部分必須再增加 --coverage-cobertura,增加後的 .gitlab-ci.yml 如下:

1
2
3
4
5
6
7
8
9
10
test:phpunit-with-command-line:
extends: .test_template
script:
- php vendor/bin/phpunit --coverage-cobertura build/cobertura.xml --log-junit build/junit.xml
artifacts:
reports:
junit: build/junit.xml
coverage_report:
coverage_format: cobertura
path: build/cobertura.xml

如上,再增加後,可以在對應的 Merge Request 馬上看到成果:

coverage visualization

如上圖,在範例的 PHP 程式碼中,刻意的不測試 isFalse 這個方法,因此方法中的原始碼呈現 No test coverage。

2. 透過 phpunit.xml.distphpunit.xml 設定 Cobertura XML 格式 Coverage 報表

同樣的在第二段中,有提到如果要將 Cobertura XML 設定在 phpunit.xml 中,則 Coverage 的部分必須增加 report 並且增加 cobertura 的段落,範例如下:

1
2
3
4
5
6
7
8
<coverage processUncoveredFiles="true">
<include>
<directory suffix=".php">./src</directory>
</include>
<report>
<cobertura outputFile="build/report.cobertura.xml"/>
</report>
</coverage>

這邊的 phpunit.xml 將 Cobertura 格式的檔案輸出到 build/report.cobertura.xml 這個檔案路徑。因此對應的 GitLab CI Pipeline 描述,也需要針對報表的路徑做調整,如下:

1
2
3
4
5
6
7
8
9
10
test:phpunit-with-phpunit-file:
extends: .test_template
script:
- php vendor/bin/phpunit
artifacts:
reports:
junit: build/report.junit.xml
coverage_report:
coverage_format: cobertura
path: build/report.cobertura.xml

3. 在 ParaTest 中產出 Cobertura XML 格式 Coverage 報表

在 PHP 語言中,為了讓 PHPUnit 可以執行的更快,開始有一些加強的方案,如很多人常用的平行化處理方案,透過多執行序來同時執行 PHP 的單元測試以加快完成速度,其中 ParaTest paratestphp/paratest: Parallel testing for PHPUnit 就是這中間很有名的一個工具。如上一篇提到 ParaTest 大部分的指令都可以與 PHPUnit 支援的相容,在經過測試後,發現 Coverage Report 產出 Cobertura XML 格式報表的部分,是相容的,因此幾乎不太需要更動。如下是在 ParaTest 的指令模式中產出 Cobertura XML 格式報表:

1
2
3
4
5
6
7
8
9
10
11
test:paratest-with-phpunit-file:
extends: .test_template
script:
- mkdir -p build
- vendor/bin/paratest --colors --processes 2 --runner=WrapperRunner
artifacts:
reports:
junit: build/report.junit.xml
coverage_report:
coverage_format: cobertura
path: build/report.cobertura.xml

上面內容與上一篇一樣 mkdir -p build 是為了避免無法寫入檔案造成無法產出報表。其他的如 PHPUnit 產出 Cobertura XML 格式報表,僅加入了 artifacts:reports:cobertura 來取得 Cobertura XML 格式報表對應的檔案。

4. 接下來就是使用 Command Line 產出報表的範例:

1
2
3
4
5
6
7
8
9
10
11
12
test:paratest-with-command-line:
extends: .test_template
script:
- mkdir -p build
- vendor/bin/paratest --colors --processes 2 --runner=WrapperRunner --coverage-cobertura build/cobertura.xml --log-junit build/junit.xml
artifacts:
reports:
junit: build/junit.xml
coverage_report:
coverage_format: cobertura
path: build/cobertura.xml

同上一個範例中,也同樣有 mkdir -p build 這段產出 build 資料夾的部分。其它的如 PHPUnit 在 Command Line 一樣,要產出 Cobertura XML 格式的報表必須要透過 --coverage-cobertura build/cobertura.xml 參數。

五、結論

在 GitLab CI 中使 PHP 的測試工作 PHPUnit 執行後產出 JUnit XML 格式的報表,主要倚靠 PHPUnit 自身的功能搭配 GitLab CI artifacts:reports:junit 機制來完成,而,如果需要更有效的看到視覺化的 Code Coverage 則可以透過 artifacts:reports:cobertura,在 PHP 的環境中,透過小祕技產出 Cobertura XML 其實只是增加一兩段指令,不會太難,最難的部分反而是從 PHPUnit 原始法中找到 coverage-cobertura 這個宣告。

希望這篇文章,希望對大家有幫助。

六、參考連結

作者: 墨嗓 (陳佑竹)
連結: https://mouson.im/Notes/GitLab/gitlab-show-php-code-coverage-in-merge-request/

当前网速较慢或者你使用的浏览器不支持博客特定功能,请尝试刷新或换用Chrome、Firefox等现代浏览器