测试覆盖率可视化
借助极狐GitLab CI/CD,您可以收集您喜欢的测试或覆盖率分析工具的测试覆盖信息,并在合并请求 (MR) 的文件差异视图中可视化此信息。允许您在合并 MR 之前查看哪些行被测试覆盖,哪些行仍然需要覆盖。
测试覆盖率可视化工作原理
收集覆盖信息是通过极狐GitLab CI/CD 的产物报告功能完成的。 您可以指定一个或多个要收集的覆盖报告,包括通配符路径。 然后系统获取所有文件中的覆盖信息并将其组合在一起。覆盖文件在后台作业中解析,因此从流水线完成到页面上的可视化加载之间可能存在延迟。
为了使覆盖分析起作用,您必须向 artifacts:reports:coverage_report
提供格式正确的 Cobertura XML 报告。
这种格式最初是为 Java 开发的,但大多数其它语言的覆盖分析框架都有插件来添加对它的支持,例如:
- simplecov-cobertura (Ruby)
- gocover-cobertura (Golang)
其它覆盖分析框架支持开箱即用的格式,例如:
- Istanbul (JavaScript)
- Coverage.py (Python)
- PHPUnit (PHP)
配置后,如果您创建一个触发收集覆盖率报告的流水线的合并请求,则覆盖率将显示在差异视图中,包括来自流水线中任何阶段的任何作业的报告。每行的覆盖率显示如下:
-
covered
(绿色):通过测试至少检查过一次的行 -
no test coverage
(橙色):已加载但从未执行的行 - 无覆盖信息:未插入或未加载的行
将鼠标悬停在覆盖栏上可提供更多信息,例如测试检查代码行的次数。
上传测试覆盖率报告不会启用:
您必须单独配置这些。
限制
Cobertura 格式 XML 文件的 100 个 <source>
节点的限制适用。如果您的 Cobertura 报告超过 100 个节点,则合并请求差异视图中可能存在不匹配或无匹配。
单个 Cobertura XML 文件不能超过 10MiB。对于大型项目,将 Cobertura XML 拆分为较小的文件。 提交许多文件时,可能需要几分钟才能在合并请求中显示覆盖范围。
可视化仅在流水线完成后显示。如果流水线有阻塞的手动作业,则流水线在继续之前等待手动作业,并且不被视为完成。 如果阻塞的手动作业未运行,则无法显示可视化。
产物过期
默认情况下,用于在合并请求上绘制可视化的流水线产物在创建后一周过期。
来自子流水线的覆盖率报告
- 引入于 15.1 版本,功能标志为
ci_child_pipeline_coverage_reports
,默认禁用。- 在 SaaS 版和私有化部署版上启用于 15.2 版本,功能标志
ci_child_pipeline_coverage_reports
已删除。
如果子流水线中的作业创建了覆盖率报告,则该报告将包含在父流水线的覆盖率报告中。
child_test_pipeline:
trigger:
include:
- local: path/to/child_pipeline_with_coverage.yml
自动类路径校正
- 引入于 13.8 版本。
- 功能标志移除于 13.9 版本。
只有当 class
元素的 filename
包含相对于项目根目录的完整路径时,覆盖率报告才能正确匹配更改的文件。但是,在某些覆盖分析框架中,生成的 Cobertura XML 具有相对于类包目录的 filename
路径。
为了对项目根相对 class
路径做出明智的猜测,Cobertura XML 解析器尝试通过以下方式构建完整路径:
- 从
sources
元素中提取部分source
路径,并将它们与类filename
路径组合。 - 检查项目中是否存在候选路径。
- 使用匹配的第一个候选者作为类完整路径。
路径校正示例
例如,一个项目具有:
-
test-org/test-project
的完整路径。 -
与项目根目录相关的以下文件:
Auth/User.cs Lib/Utils/User.cs src/main/java
位于:
-
Cobertura XML 中,
class
元素中的filename
属性假定该值是项目根目录的相对路径:<class name="packet.name" filename="src/main/java" line-rate="0.0" branch-rate="0.0" complexity="5">
-
来自 Cobertura XML 的
sources
,格式为<CI_BUILDS_DIR>/<PROJECT_FULL_PATH>/...
的以下路径:<sources> <source>/builds/test-org/test-project/Auth</source> <source>/builds/test-org/test-project/Lib/Utils</source> </sources>
解析器:
- 从
sources
中提取Auth
和Lib/Utils
并使用它们来确定相对于项目根目录的class
路径。 - 结合这些提取的
sources
和类文件名。例如,有一个class
元素的filename
值为User.cs
,则解析器会采用第一个匹配的候选路径,即Auth/User.cs
。 - 对于每个
class
元素,尝试为每个提取的source
路径查找匹配项,最多 100 次迭代。如果在文件树中没有找到匹配的路径就达到了这个限制,那么这个类就不会包含在最终的覆盖率报告中。
<CI_BUILDS_DIR>/<PROJECT_FULL_PATH>/...
的 source
路径。
如果路径不遵循此模式,则忽略 source
。解析器假定 class
元素的 filename
包含相对于项目根目录的完整路径。示例测试覆盖率配置
本节提供不同编程语言的测试覆盖率配置示例。
JavaScript 示例
以下 .gitlab-ci.yml
示例使用 Mocha JavaScript 测试和 nyc 覆盖率工具生成覆盖率产物:
test:
script:
- npm install
- npx nyc --reporter cobertura mocha
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml
Java and Kotlin 示例
Maven 示例
以下用于 Java 或 Kotlin 的 .gitlab-ci.yml
示例使用 Maven 来构建项目和 JaCoCo 覆盖率工具来生成覆盖率产物。
极狐GitLab 需要 Cobertura 格式的产物,因此您必须在上传之前执行一些脚本。test-jdk11
作业测试代码并生成 XML 工件。coverage-jdk-11
作业将产物转换为 Cobertura 报告:
test-jdk11:
stage: test
image: maven:3.6.3-jdk-11
script:
- mvn $MAVEN_CLI_OPTS clean org.jacoco:jacoco-maven-plugin:prepare-agent test jacoco:report
artifacts:
paths:
- target/site/jacoco/jacoco.xml
coverage-jdk11:
# Must be in a stage later than test-jdk11's stage.
# The `visualize` stage does not exist by default.
# Please define it first, or choose an existing stage like `deploy`.
stage: visualize
image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.7
script:
# convert report from jacoco to cobertura, using relative project path
- python /opt/cover2cover.py target/site/jacoco/jacoco.xml $CI_PROJECT_DIR/src/main/java/ > target/site/cobertura.xml
needs: ["test-jdk11"]
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: target/site/cobertura.xml
Gradle 示例
以下用于 Java 或 Kotlin 的 .gitlab-ci.yml
示例使用 Gradle 构建项目和 JaCoCo 覆盖率工具来生成覆盖率产物。
极狐GitLab 需要 Cobertura 格式的产物,因此您必须在上传之前执行一些脚本。test-jdk11
作业测试代码并生成 XML 产物。coverage-jdk-11
作业将产物转换为 Cobertura 报告:
test-jdk11:
stage: test
image: gradle:6.6.1-jdk11
script:
- 'gradle test jacocoTestReport' # jacoco must be configured to create an xml report
artifacts:
paths:
- build/jacoco/jacoco.xml
coverage-jdk11:
# Must be in a stage later than test-jdk11's stage.
# The `visualize` stage does not exist by default.
# Please define it first, or chose an existing stage like `deploy`.
stage: visualize
image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.7
script:
# convert report from jacoco to cobertura, using relative project path
- python /opt/cover2cover.py build/jacoco/jacoco.xml $CI_PROJECT_DIR/src/main/java/ > build/cobertura.xml
needs: ["test-jdk11"]
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: build/cobertura.xml
Python 示例
以下 Python 的 .gitlab-ci.yml
示例使用 pytest-cov 收集测试覆盖率数据,coverage.py 将报告转换为使用完整的相对路径。没有转换就不会显示信息。
此示例假设您的包的代码在 src/
中并且您的测试在 tests.py
中:
run tests:
stage: test
image: python:3
script:
- pip install pytest pytest-cov
- coverage run -m pytest
- coverage report
- coverage xml
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
PHP 示例
以下 PHP 的 .gitlab-ci.yml
示例使用 PHPUnit 来收集测试覆盖率数据并生成报告。
使用最小的 phpunit.xml
文件,您可以运行测试并生成 coverage.xml
:
run tests:
stage: test
image: php:latest
variables:
XDEBUG_MODE: coverage
before_script:
- apt-get update && apt-get -yq install git unzip zip libzip-dev zlib1g-dev
- docker-php-ext-install zip
- pecl install xdebug && docker-php-ext-enable xdebug
- php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
- php composer-setup.php --install-dir=/usr/local/bin --filename=composer
- composer install
- composer require --dev phpunit/phpunit phpunit/php-code-coverage
script:
- php ./vendor/bin/phpunit --coverage-text --coverage-cobertura=coverage.cobertura.xml
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.cobertura.xml
Codeception,通过 PHPUnit,也支持用 run
生成 Cobertura 报告。生成文件的路径取决于单元测试套件的 --coverage-cobertura
选项和 paths
配置。配置 .gitlab-ci.yml
在适当的路径中找到 Cobertura。
C/C++ 示例
以下 .gitlab-ci.yml
示例适用于带有 gcc
或 g++
的 C/C++,因为编译器使用 gcovr
生成 Cobertura XML 格式的覆盖率输出文件。
此示例假设:
-
Makefile
是在前一阶段的另一个作业中,由cmake
在build
目录中创建的。(如果使用automake
生成Makefile
,则需要调用make check
而不是make test
。) -
cmake
(或automake
)设置了编译器选项--coverage
。
run tests:
stage: test
script:
- cd build
- make test
- gcovr --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml --root ${CI_PROJECT_DIR}
coverage: /^\s*lines:\s*\d+.\d+\%/
artifacts:
name: ${CI_JOB_NAME}-${CI_COMMIT_REF_NAME}-${CI_COMMIT_SHA}
expire_in: 2 days
reports:
coverage_report:
coverage_format: cobertura
path: build/coverage.xml
Go 示例
以下 Go 使用的 .gitlab-ci.yml
示例:
-
go test
运行测试。 -
gocover-cobertura
将 Go 的覆盖率配置文件转换为 Cobertura XML 格式。
此示例假定正在使用 Go 模块。请注意,-covermode count
选项不适用于 -race
标志。
如果您想在使用 -race
标志的同时生成代码覆盖率,则必须切换到比 -covermode count
慢的-covermode atomic
。有关详细信息,请参阅 此博客文章。
run tests:
stage: test
image: golang:1.17
script:
- go install
- go test ./... -coverprofile=coverage.txt -covermode count
- go get github.com/boumenot/gocover-cobertura
- go run github.com/boumenot/gocover-cobertura < coverage.txt > coverage.xml
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage.xml
Ruby 示例
以下用于 Ruby 的 .gitlab-ci.yml
示例使用:
-
rspec
运行测试。 -
simplecov
和simplecov-cobertura
记录覆盖率概况以及创建 Cobertura XML 格式的报告。
此示例假设:
-
bundler
被用于依赖管理。rspec
、simplecov
和simplecov-cobertura
gem 已添加到您的Gemfile
中。 -
CoberturaFormatter
已添加到spec_helper.rb
文件中的SimpleCov.formatters
配置中。
run tests:
stage: test
image: ruby:3.1
script:
- bundle install
- bundle exec rspec
artifacts:
reports:
coverage_report:
coverage_format: cobertura
path: coverage/coverage.xml
故障排除
未显示测试覆盖率可视化
如果测试覆盖率可视化未显示在差异视图中,您可以检查覆盖率报告本身并验证:
- 您在差异视图中查看的文件在覆盖率报告中提及。
- 报告中的
source
和filename
节点遵循预期结构,匹配仓库中的文件。
默认情况下,报告产物不可下载。如果您希望可以从作业详细信息页面下载报告,请将您的覆盖率报告添加到产物 paths
中:
artifacts:
paths:
- coverage/cobertura-coverage.xml
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml