Coveralls に以下のドキュメントがあるのだけど、書いてある通りにやってもうまくいかなかったので、なんとかうまくいった方法を書いておきます。
やりたいこと
RSpec を並列で実行し、すべてのインスタンスの終了を待って、別のジョブで各インスタンスのカバレッジの結果をマージして Coveralls に送信したい。
-
ドキュメントには
Coveralls.wear_merged!
を呼んで最後にcoveralls:push
を実行しろとあるけれど、coveralls:push
(Coveralls.push!
) で使われているSimpleCov::ResultMerger.merged_result
は 同じ環境で実行された 複数のテストのカバレッジ結果をマージするものであり、違う環境で実行された結果をマージできるわけではない。 -
Webhookを使う方法もあるけれど、テストを実行するジョブと結果を送信するジョブが違うので、結果を送信するときに
payload[build_num]
を指定することができない。
やり方
Gemfile
group :test do
gem 'coveralls', '~> 0.8.23', require: false
end
RSpec
Coveralls.wear!
は Coveralls にカバレッジを送信するけれど、Coveralls.wear_merged!
は送信しない(SimpleCov を実行してるだけ)。
spec/rails_helper.rb
if ENV['COVERALLS_REPO_TOKEN']
require 'coveralls'
Coveralls.wear_merged!('rails')
end
# [...]
カバレッジ結果をマージ・送信する Rake Task をつくる
SimpleCov の README にある "Merging test runs under different execution environments" の通りにやればいいのだけど、Coveralls の gem が依存している SimpleCov のバージョンが古くて、SimpleCov.collate
が存在しない。よって自分でつくる必要がある。
lib/tasks/coverage.rake
namespace :coverage do
desc 'Collate all result sets and push to Coveralls'
task push: %i[environment collate] do
require 'coveralls'
Coveralls.push!
end
desc 'Collate all result sets generated by the different test runners'
task collate: :environment do
require 'simplecov'
# coverage/.resultset-*.json を coverage/.resultset.json にまとめる
# see: https://github.com/colszowka/simplecov/blob/v0.18.5/lib/simplecov.rb#L80
results = Dir['coverage/.resultset-*.json'].flat_map do |filename|
(JSON.parse(File.read(filename)) || {}).map do |command_name, coverage|
SimpleCov::Result.from_hash(command_name => coverage)
end
end
SimpleCov::ResultMerger.merge_results(*results).tap do |result|
SimpleCov::ResultMerger.store_result(result)
end
end
end
CircleCI
.circleci/config.yml
version: 2.1
executors:
app:
docker:
- image: circleci/ruby:2.6.5-node-browsers
environment:
BUNDLE_PATH: vendor/bundle
RAILS_ENV: test
jobs:
test:
executor: app
parallelism: 2
steps:
- checkout
# [...]
- persist_to_workspace:
root: .
paths:
- .
- run:
name: RSpec in parallel
command: bin/rspec $(circleci tests glob "spec/**/*_spec.rb" | circleci tests split --split-by=timings)
# coverage/.resultset.json にカバレッジ結果が生成されているので、
# 名前がぶつからないように別の名前を付けたファイルを persist_to_workspace する
- run:
name: Prepare to collate coverage results
command: cp coverage/.resultset.json "coverage/.resultset-${CIRCLE_NODE_INDEX}.json"
- persist_to_workspace:
root: .
paths:
- coverage/.resultset-*.json
report:
executor: app
steps:
- attach_workspace:
at: .
# こんな感じになっているはず
# ./coverage
# .resultset-0.json
# .resultset-1.json
- run: bin/rake coverage:push
workflows:
version: 2
test-report:
jobs:
- test
- report:
requires:
- test
できた
#!/bin/bash -eo pipefail
bin/rake coverage:push
Running via Spring preloader in process 81
[Coveralls] Submitting to https://coveralls.io/api/v1
[Coveralls] Job #132.1
[Coveralls] https://coveralls.io/jobs/XXXXXXXX
Coverage is at 100.0%.
Coverage report sent to Coveralls.
CircleCI received exit code 0