くりにっき

フルスタックキュアエンジニアです

Goで複数パッケージ構成のプロジェクトでもちゃんとカバレッジ集計する(GitLab編)

tl;dr;

gcov + lcovが無難

Goで複数パッケージ構成の場合何が問題か?

Goでテストを実行する時に -cover をつけるとお手軽にカバレッジが集計できるのですが、1パッケージずつしか出力されないので複数パッケージ構成のプロジェクトの時にプロジェクト全体のカバレッジが集計できずに困ります。*1

$ go test -count=1 -cover ./...

ok      gitlab.com/sue445/tanuki_reminder   0.066s  coverage: 77.2% of statements
ok      gitlab.com/sue445/tanuki_reminder/enum  0.026s  coverage: 100.0% of statements

GitHubの場合

songmu.jp

にもあるように

  1. -coverprofile coverage.out でgcov形式で出力
  2. jandelgado/gcov2lcov-action でlcov形式に変換
  3. lcovファイルをCoverallsに送信

が今の所最適解だと思います。

GitLabの場合

GitHubと違いGitLabはカバレッジ集計に標準対応しているというのがあります。

https://docs.gitlab.com/ee/ci/pipelines/settings.html#test-coverage-parsing

CIのログに出力されたテストカバレッジっぽい文字列を正規表現で抽出するという割と力技な仕組なのですが、各言語のデファクトツールに関してはだいたい正規表現が用意されています。

f:id:sue445:20201108170855p:plain

GitLabのリポジトリ上で正規表現を設定しておくだけで最終的にリポジトリ上でカバレッジの推移のグラフが見れます。 *2

f:id:sue445:20201108171107p:plain

Coverallsに比べればだいぶ貧弱なのですがリポジトリに標準機能でついているという強みはあります。

もしこれで困る場合はCoverallsのような他ツールを使うか*3、lcovで生成したカバレッジレポートをGitLab Pagesにアップロードすればいいと思います。

本題:GitLabで複数パッケージ構成のGoプロジェクトのカバレッジを集計したい

tanuki_reminder だと当初 /coverage: \d+.\d+% of statements/ のような正規表現カバレッジを収集してたのですが、1つのログに複数のカバレッジが出ていると後の方にあるものがそのジョブのカバレッジとして採用されて複数パッケージ構成になった時に変なことになりました。*4

f:id:sue445:20201108172019p:plain

いくつか手法を考えたのですが、lcovのgenhtmlコマンドでHTML形式のカバレッジレポートを作る時にプロジェクト全体のカバレッジがログに出力されることに着目してこいつを利用するようにしました

f:id:sue445:20201108173154p:plain

.gitlab-ci.ymlのサンプル

test:
  image: golang:1.15

  stage: test

  variables:
    GCOV2LCOV_VERSION: v1.0.4

  script:
    - mkdir -p coverage/
    - go test -coverprofile coverage/coverage.out -covermode atomic ./...

    # Install and run gcov2lcov
    - wget https://github.com/jandelgado/gcov2lcov/releases/download/${GCOV2LCOV_VERSION}/gcov2lcov-linux-amd64.tar.gz -q -O - | tar xvzf - --strip 1
    - chmod 755 gcov2lcov-linux-amd64
    - export GOROOT=$(go env GOROOT)
    - ./gcov2lcov-linux-amd64 -infile "coverage/coverage.out" -outfile "coverage/coverage.lcov"

    # Install and run lcov
    - apt-get update
    - apt-get install -y lcov
    - genhtml coverage/coverage.lcov -o coverage/

  # regular expression for genhtml
  coverage: '/lines.*\d+\.\d+%/'

  artifacts:
    paths:
      - coverage/

lcovを使ってるので後半部分はGo以外でも流用できそう