hacomono TECH BLOG

フィットネスクラブ・スクールなど施設・店舗のための会員管理・予約・決済システム「hacomono」 開発チームの技術ブログ

コードカバレッジを測定してみた

最近ポケモンメザスタにハマっている、プロダクト開発チームのまっつん(@pagu_o28)です。

プロダクトの品質向上のために、コードカバレッジもテストコード自体の質も高い状態にしていきたいです。

hacomono のバックエンドは Ruby on Rails で、テストフレームワークは rspec です。今回はバックエンド側のコードカバレッジの測定について書きます。

1. カバレッジレポートを確認できる状態にする

まずは現状の確認をしました。

simplecov を導入済みでしたが、何故かちゃんと計測できていない状態でした。

なぜ計測できていないのかを調査するところからはじめました。

SimpleCovのREADME を確認してみる

よく読んでみると、下記の記述はアプリケーションのコードを require する前に呼んでおくこと!と書いてありました。

require 'simplecov'
SimpleCov.start

# Previous content of test helper now starts here

アプリケーションのコードを確認してみる

アプリケーションのコードを require した後に SimpleCov.start を呼んでいました。 そのため README 通りに読み込みの順番を調整したところ、アプリケーションコードが計測対象として上がってきました!

2. CIでカバレッジレポートを生成して確認できる状態にする

ローカル環境で全テストコードを毎回実行してカバレッジレポートを見るようにする、という運用ルールはなかなか厳しいと思います。
ということで次に CI 実行時にカバレッジレポートを出力するようにしたいと思います。

CircleCIでのテスト実行について

hacomonoではバックエンド側(API)のテストは CircleCI で並列実行しています。 コンテナ0, コンテナ1,コンテナ2とそれぞれのコンテナで rspec が実行されカバレッジレポートがコンテナ上に出力されます。 そのため各コンテナのレポートをそれぞれ確認しなければなりません。 また、実行されたテストに基づいてカバレッジレポートが作られるので、コンテナ0番ではAファイルのカバレッジが低いけど、コンテナ1番では高い、みたいな状況が生まれてしまいます。

それを解消するために各コンテナのカバレッジレポートを1つにまとめる必要があります。
SimpleCovにcollateというメソッドが用意されていて、それを使ってまとめることができます。
また、CircleCI側でもジョブ間でファイルを共有する仕組みが用意されているのでそれを利用します。

SimpleCovでテスト結果をまとめるバッチを作成する

SimpleCovを実行してカバレッジレポートが作成されると下記のようなファイル群が出力されます。
この .resultset.json をマージしていきます。

tree -a -L 1
.
├── .last_run.json
├── .resultset.json <- これ!
├── .resultset.json.lock
├── assets
└── index.html

READMEに記載されているコードを参考に複数カバレッジの結果ファイルをマージするバッチを作ります。

https://github.com/simplecov-ruby/simplecov#merging-test-runs-under-different-execution-environments

# @path_pattern はこんな感じ。 "/tmp/simplecov-resultsets/.resultset-*.json"
SimpleCov.collate Dir[@path_pattern] do
  add_group 'Controllers', 'app/controllers'
  add_group 'Data', 'layer/data'
  add_group 'Domain', 'layer/domain'
  add_group 'Helper', 'layer/helper'
  add_group 'Infra', 'layer/infra'
  add_group 'Presentation', 'layer/presentation'
  add_group 'lib', 'lib'
  coverage_dir Rails.root.join('tmp', 'simplecov_report')
end

CircleCI でジョブ間でファイルを共有し結果ファイルを作成する

rspec 実行後に .resultset.jsonを名前を変更してコピーしておきます。 その後、 persist_to_workspaceを使ってワークスペースにコピーしたファイルを追加しておきます。
CircleCI で利用可能な定義済の変数についてはこちらです。
https://circleci.com/docs/ja/built-in-environment-variables/

# rspec を実行する
- run:
    ...

# rspec 実行後に生成された .resultset.json ファイルを名前を変更してコピーしておいく。
- run:
    name: copy coverage result
    command: |
      mkdir /tmp/simplecov-resultsets
      cp tmp/coverage/.resultset.json /tmp/simplecov-resultsets/.resultset-${CIRCLE_NODE_INDEX}.json
      working_directory: api

# ワークスペースにファイルを追加する
- persist_to_workspace:
    root: /tmp
    paths:
      - simplecov-resultsets

次に先程作成したSimpleCovのレポートをマージするバッチを実行するジョブの設定を書きます。

coverage_report:
  working_directory: ~/repo
  resource_class: medium
  steps:

       # ワークスペースに追加したファイルをダウンロードする
    - attach_workspace:
        at: /tmp

    # 先程作ったマージするバッチタスクを実行する
    - run:
        name: Merge coverage report.
        command: RAILS_ENV=ci bin/rake mf:coverage_report_merge
        working_directory: api

    # ファイルをアップロードする
    - store_artifacts:
        path: api/tmp/simplecov_report

これはテストジョブ成功後に実行されるようにしたいので、このような書き方をしておきます。

jobs:
  - api_test
  - coverage_report:
      requires:
        - api_test

この状態で CI を回します。

するとAPIのテストが成功していて、カバレッジレポート出力も成功します。

CircleCIのARTIFACTSタブにindex.htmlがあるのでそれをクリックします。

SimpleCovのレポート結果の確認画面が開けました! これでカバレッジレポートが CircleCI 上で確認できるようになりました!

まとめ

CircleCI 上で確認できるようになったのは良いものの、まだ課題もあります。

  • 開発者が CircleCI の ARTIFACTS を見に行かなければならない。
  • Github の PR で変更したファイルのカバレッジをレポートファイルから探して確認しなければならない。

ということで今後またこういう時間が取れたら Github 上でカバレッジの確認を完結できるような仕組みを作りたいです。

余談ですが、 GitLab では Test coverage visualization のような仕組みがあるようで、良いなーと思いながら作業してました。

参考

https://github.com/simplecov-ruby/simplecov
https://docs.gitlab.com/ee/ci/testing/test_coverage_visualization.html
https://qiita.com/kazutosato/items/1ff33fc86151ca78518f