테스트 커버리지 시각화
GitLab CI/CD를 사용하여 즐겨 사용하는 테스트 또는 커버리지 분석 도구의 테스트 커버리지 정보를 수집하고 Merge Request(MR)의 파일 차이 보기에서 이 정보를 시각화할 수 있습니다. 이를 통해 MR이 Merge되기 전에 어떤 라인이 테스트에 의해 커버되었고 어떤 라인이 아직 커버되어야 하는지 확인할 수 있습니다.
테스트 커버리지 시각화 작동 방식
커버리지 정보 수집은 GitLab CI/CD artifact reports 기능을 사용하여 수행됩니다. 와일드카드 경로를 포함한 하나 이상의 커버리지 보고서를 지정할 수 있습니다. 그런 다음 GitLab은 모든 파일의 커버리지 정보를 모아서 결합합니다. 커버리지 파일은 백그라운드 작업에서 구문 분석되므로 파이프라인 완료 후 시각화가 페이지에 로드되기까지 지연될 수 있습니다.
커버리지 분석이 작동하려면 적절한 형식의 Cobertura XML 보고서를 제공해야 합니다. 이 형식은 원래 Java용으로 개발되었지만 대부분의 다른 언어용 커버리지 분석 프레임워크에는 이를 지원하기 위한 플러그인이 있습니다. 예를 들어:
- simplecov-cobertura (Ruby)
- gocover-cobertura (Go)
다른 커버리지 분석 프레임워크는 예를 들어 다음과 같이 형식을 기본적으로 지원합니다:
- Istanbul (JavaScript)
- Coverage.py (Python)
- PHPUnit (PHP)
구성 후, Merge Request이 커버리지 보고서를 수집하는 파이프라인을 트리거하면 커버리지 정보가 파일 차이 보기에 표시됩니다. 이는 파이프라인의 어떤 스테이지의 어떤 작업에서든 보고서가 포함됩니다. 커버리지는 각 라인에 대해 다음을 표시합니다:
-
covered
(녹색): 테스트에 의해 한 번 이상 확인된 라인 -
no test coverage
(주황색): 로드되었지만 실행되지 않은 라인 - 커버리지 정보 없음: 실험적이거나 로드되지 않은 라인
커버리지 막대에 마우스를 올리면 라인을 테스트한 횟수 등과 같은 자세한 정보가 제공됩니다.
테스트 커버리지 보고서 업로드는 Merge Request에서 다음 기능을 활성화하지 않습니다:
이러한 기능을 따로 구성해야 합니다.
제한 사항
Cobertura 형식 XML 파일에 대한 <source>
노드 수는 100개로 제한됩니다. Cobertura 보고서가 100개의 노드를 초과하는 경우 Merge Request에서 불일치 또는 일치하지 않는 현상이 발생할 수 있습니다.
단일 Cobertura XML 파일의 크기 제한은 10 MiB입니다. 큰 프로젝트의 경우 Cobertura XML을 더 작은 파일로 분할하세요. 자세한 사항은 이 이슈를 참조하세요. 많은 파일을 제출하면 Merge Request에서 커버리지가 표시되는 데 몇 분이 걸릴 수 있습니다.
시각화는 파이프라인이 완료된 후에만 표시됩니다. 파이프라인이 차단 매뉴얼 작업을 가지고 있는 경우 파이프라인은 차단 매뉴얼 작업이 실행되기 전까지 기다리고 완료로 간주되지 않습니다. 시각화는 차단 매뉴얼 작업이 실행되지 않은 경우에는 표시할 수 없습니다.
데이터 만료
- GitLab 13.12에서 소개됨, 최신 데이터는 만료 시간에 관계없이 유지됩니다.
기본적으로 Merge Request에서 시각화를 그리기 위해 사용된 데이터는 생성 후 1주일 후에 만료됩니다.
자식 파이프라인에서의 커버리지 보고서
- GitLab 15.1에서 플래그(Flag)와 함께 소개됨. 기본으로 비활성화됨.
- GitLab 15.2에서 GitLab.com 및 Self-managed에서 활성화됨 및
ci_child_pipeline_coverage_reports
피처 플래그 제거됨.
자식 파이프라인의 작업이 커버리지 보고서를 생성하는 경우 해당 보고서는 부모 파이프라인의 커버리지 보고서에 포함됩니다.
child_test_pipeline:
trigger:
include:
- local: path/to/child_pipeline_with_coverage.yml
자동 클래스 경로 수정
변경된 파일과 적절히 일치하는 커버리지 보고서는 class
요소의 filename
에 클래스 패키지 디렉터리에 상대적인 전체 경로가 포함된 경우에만 올바르게 일치합니다. 그러나 일부 커버리지 분석 프레임워크에서 생성된 Cobertura XML에는 클래스 filename
경로가 클래스 패키지 디렉터리에 상대적인 경로로 존재합니다.
Cobertura XML 파서는 프로젝트 루트상대적 인 class
경로를 지능적으로 추측하기 위해 다음을 시도합니다:
-
sources
요소에서source
경로의 일부를 추출하고 클래스filename
경로와 결합합니다. - 후보 경로가 프로젝트에 존재하는지 확인합니다.
- 일치하는 후보를 클래스 전체 경로로 사용합니다.
경로 수정 예시
예를 들어, 다음과 같은 C# 프로젝트가 있다고 가정합니다:
-
test-org/test-cs-project
전체 경로. -
다음은 프로젝트 루트에 상대적인 파일입니다:
Auth/User.cs Lib/Utils/User.cs
-
Cobertura XML의
sources
, 다음 형식의 경로:<sources> <source>/builds/test-org/test-cs-project/Auth</source> <source>/builds/test-org/test-cs-project/Lib/Utils</source> </sources>
파서:
-
sources
에서Auth
및Lib/Utils
를 추출하고 이를 사용하여 프로젝트 루트에 상대적인class
경로를 결정합니다. - 추출된
sources
를 클래스filename
와 결합합니다. 예를 들어,User.cs
라는class
요소가 있으면 파서는 파일 트리에서 일치하는 경로를 찾아 첫 번째 후보 경로를 가져옵니다(예:Auth/User.cs
). - 각
class
요소에 대해 파일 트리에서 최대 100회 일치하는 경로를 찾아보고, 파일 트리에서 일치하는 경로를 찾을 때까지 시도합니다. 일치하는 경로를 찾지 못하면 최종 커버리지 보고서에 클래스가 포함되지 않습니다.
자동 클래스 경로 수정은 다음과 같은 Java 프로젝트에서도 작동합니다:
-
test-org/test-java-project
전체 경로. -
다음은 프로젝트 루트에 상대적인 파일입니다:
src/main/java/com/gitlab/security_products/tests/App.java
-
Cobertura XML의
sources
:<sources> <source>/builds/test-org/test-java-project/src/main/java/</source> </sources>
-
filename
값이com/gitlab/security_products/tests/App.java
인class
요소:<class name="com.gitlab.security_products.tests.App" filename="com/gitlab/security_products/tests/App.java" line-rate="0.0" branch-rate="0.0" complexity="6.0">
자동 클래스 경로 수정은 <CI_BUILDS_DIR>/<PROJECT_FULL_PATH>/...
형식의 source
에 대해서만 작동합니다.
이 형식을 따르지 않는 경우 source
는 무시됩니다. 파서는 class
요소의 filename
에 프로젝트 루트에 상대적인 전체 경로가 포함되었다고 가정합니다.
테스트 커버리지 구성 예제
이 섹션에서는 다양한 프로그래밍 언어에 대한 테스트 커버리지 구성 예제를 제공합니다.
또한 작동하는 예제는 coverage-report
데모 프로젝트에서 볼 수 있습니다.
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 및 Kotlin 예제
Maven 예제
다음은 Java 또는 Kotlin을 위한 .gitlab-ci.yml
예제로, 프로젝트를 빌드하고 커버리지 도구로 JaCoCo를 사용하여 커버리지 아티팩트를 생성합니다.
자체 이미지를 빌드하려면 Docker 이미지 구성 및 스크립트를 확인할 수 있습니다.
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:
# 반드시 test-jdk11 단계보다 나중에 있어야 합니다.
# `visualize` 단계는 기본적으로 존재하지 않습니다.
# 먼저 정의하거나 `deploy`와 같은 기존 단계 중 하나를 선택해야 합니다.
stage: visualize
image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.9
script:
# 상대 프로젝트 경로를 사용하여 jacoco에서 cobertura 보고서로 변환
- 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
예제로, 프로젝트를 빌드하고 커버리지 도구로 JaCoCo를 사용하여 커버리지 아티팩트를 생성합니다.
자체 이미지를 빌드하려면 Docker 이미지 구성 및 스크립트를 확인할 수 있습니다.
GitLab은 아티팝트를 Cobertura 형식으로 예상하므로 업로드하기 전에 몇 가지 스크립트를 실행해야 합니다.
test-jdk11
작업은 코드를 테스트하고 XML 아티팩트를 생성합니다.
coverage-jdk-11
작업은 아티팩트를 Cobertura 보고서로 변환합니다.
test-jdk11:
stage: test
image: gradle:6.6.1-jdk11
script:
- 'gradle test jacocoTestReport' # jacoco가 xml 보고서를 생성하도록 설정되어야 합니다.
artifacts:
paths:
- build/jacoco/jacoco.xml
coverage-jdk11:
# 반드시 test-jdk11 단계보다 나중에 있어야 합니다.
# `visualize` 단계는 기본적으로 존재하지 않습니다.
# 먼저 정의하거나 `deploy`와 같은 기존 단계 중 하나를 선택해야 합니다.
stage: visualize
image: registry.gitlab.com/haynes/jacoco2cobertura:1.0.7
script:
# 상대 프로젝트 경로를 사용하여 jacoco에서 cobertura 보고서로 변환
- 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 예제
다음 .gitlab-ci.yml
예제는 pytest-cov를 사용하여 테스트 커버리지 데이터를 수집합니다.
run tests:
stage: test
image: python:3
script:
- pip install pytest pytest-cov
- pytest --cov --cov-report term --cov-report xml: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 구성에 따라 unit test suite에 대한 .gitlab-ci.yml
을 구성하세요.
C/C++ 예제
다음 .gitlab-ci.yml
은 C/C++를 위한 예제이며, 컴파일러로 gcc
또는 g++
를 사용하며 gcovr
을 사용하여 Cobertura XML 형식의 커버리지 출력 파일을 생성합니다.
이 예제는 다음을 가정합니다:
-
Makefile
이cmake
에 의해build
디렉터리에 생성되었다고 가정하며, 이는 이전 스테이지의 다른 작업 내부에서 수행됩니다. (만약automake
를 사용하여Makefile
을 생성한다면,make test
대신make check
를 호출해야 합니다.) -
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 예제
다음 .gitlab-ci.yml
은 Go를 위한 예제로, 다음을 사용합니다:
- 테스트를 실행하는 데
go test
를 사용합니다. - 고의 커버리지 프로필을 Cobertura XML 형식으로 변환하는 데
gocover-cobertura
를 사용합니다.
이 예제는 Go 모듈이 사용된다고 가정합니다. -covermode count
옵션은 -race
플래그와 함께 작동하지 않습니다.
-race
플래그를 사용하면서 코드 커버리지를 생성하려면 -covermode atomic
로 전환해야 하며, 이는 -covermode count
보다 느립니다. 자세한 내용은 이 블로그 포스트를 참조하세요.
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 예제
다음 .gitlab-ci.yml
은 Ruby를 위한 예제이며, 다음을 사용합니다:
- 테스트를 실행하는 데
rspec
을 사용합니다. - 커버리지 프로필을 기록하고 Cobertura XML 형식으로 보고서를 생성하는 데
simplecov
와simplecov-cobertura
를 사용합니다.
이 예제는 다음을 가정합니다:
- 의존성 관리에
bundler
가 사용된다는 것입니다.rspec
,simplecov
,simplecov-cobertura
젬이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
노드가 원하는 예상 구조를 따라 리포지터리의 파일과 일치하는지 확인합니다. - 파이프라인이 완료되었는지 확인합니다. 파이프라인이 매뉴얼 작업 대기 중이면, 파이프라인은 완료로 간주되지 않습니다.
- 커버리지 보고서 파일이 제한 사항을 초과하지 않았는지 확인합니다.
보고서 artifact는 기본적으로 다운로드할 수 없습니다. 작업 세부 정보 페이지에서 보고서를 다운로드할 수 있게 하려면, 보고서를 artifact 경로
에 추가하세요.
artifacts:
paths:
- coverage/cobertura-coverage.xml
reports:
coverage_report:
coverage_format: cobertura
path: coverage/cobertura-coverage.xml