アクトインディ開発者ブログ

子供とお出かけ情報「いこーよ」を運営する、アクトインディ株式会社の開発者ブログです

Simplecovで一部クラスでカバレッジが計測されない場合の解決方法

morishitaです。

アクトインディではRailsアプリケーションのテストをRSpecで書いています。
そして、テストカバレッジをSimpleCovを使って計測しています。

何故かカバレッジが計測できないクラスがあったのですが、計測できるように解決した件を紹介します。

tl;dr

  • SimpleCov.startは次のコードの前に実行する
require File.expand_path('../../config/environment', __FILE__)
  • .simplecov ファイルにSimpleCov.startを実行するコードを書いてRSpecの起動するクリプトで最初にrequire 'simplecov'したほうが楽。

改善前の状況

例えばいこレポではこんな感じでプルリクエストにPushするとテストが実行され、 その結果がカバレッジとともにSlackに通知される仕組みになっています1

f:id:HeRo:20190513211431p:plain
改善前

そこそこ高いカバレッジを維持できているのですが、 一部どうしても計測できないコードがあって悩んでいました。

f:id:HeRo:20190513211530p:plain
改善前 モデル

Userモデルはログインユーザを表します。
なのでテストで使わないわけがないのですが 0.0 % となっています。

SimpleCov.start の位置が重要

結論から言うと計測したいクラスがロードされるよりも先に SimpleCov.startが実行される必要があります。

そんなつもりはないのに、SimpleCov.startの前にロードしちゃっているありがちな例は次のケースです。

  1. FactoryBotのFactoryのStatic attributesでロードしてしまっている
  2. Initilizerでロードしてしまっている

1.のStatic attributes は FactoryBot 5.0以降で廃止されているので、最新のFactoryBotを使っていれば関係ないはずです。
Static attributes ってどんなの? って方はDeprecating static attributes in factory_bot 4.11を参照ください。対処法も載っています。

2.のケースですがrspec-railsを利用している場合、あんまり意識しなくても、spec_helper.rb にSimplecovの設定を実装していればテスト対象のコードより先にロードされます。 というのもrails generate rspec:installコマンドで生成される rails_helper.rbは最初にspec_helper.rb をロードするようになっているからです。

しかし、rspec-railsの3.6.0未満のバージョンで生成したrails_helper.rbを使っているとハマる可能性があります。私はこれにやられました。

解決するにはrails_helper.rbの次のコードより前にSimpleCov.startを実行すれば良いです。

require File.expand_path('../../config/environment', __FILE__)

つまり、rails_helper.rbの上記コード行より前でspec_helper.rbrequireしてやればいいのです。

test-queue

一方、アクトインディではCI環境だと test-queueを使ってRSpecを並列実行しています。 その場合、rspecコマンドではなく、TestQueue::Runner::RSpecを継承したクラスを実装して独自のRSpec起動スクリプトを用意して使います。そこでもハマっていました。

その起動スクリプト内でも次のコードが存在していました。

require File.expand_path('../../config/environment', __FILE__)

特に test-queueSimpleCov.start を実行しなくても、rails_helper.rbは各specファイルでrequireしていて読み込まれます。
それで見落としがちなのですが、やはりここでも上記コードより前にSimpleCov.startしてやる必要あるのです。

.simplecovによる設定

SimpleCov.startを実行すると言っても1行書くだけではないと思います。

例えば、上記のような設定で計測したいとします。

  • 結果出力のフォーマッタを指定
  • Railsの標準的な設定を導入
  • 7行以下のファイルは無視する

すると次のようなコードが必要になります。

require 'simplecov-json'
SimpleCov.formatters = [
  SimpleCov::Formatter::HTMLFormatter,
  SimpleCov::Formatter::JSONFormatter,
]
SimpleCov.start 'rails' do
  add_filter do |source_file|
    source_file.lines.count < 7
  end
end

前述の通り、最初に実行する必要があるため、複数のRSpec起動するスクリプトがあるとそれぞれに記載する必要があります。
それぞれにコピー&ペーストするのはDRYでないのでファイルを分けてrequireすると思います。
しかし、.simplecovという名前でプロジェクトのルートに置けばrequire 'simplecov'simplecov のロードと同時に .simplecov の内容を実行してくれます。
各起動スクリプトの最初でrequire 'simplecov'してやればいいだけなので、この方法で設定したほうがシンプルでいいと思います。

改善した結果

さて、こうして改善した結果を確認してみましょう。

f:id:HeRo:20190513211607p:plain
改善後

お、カバレッジが少し大きくなりました。
で、問題のUserクラスはというと…。

f:id:HeRo:20190513211626p:plain
改善後 モデル

やりました! 計測できています!

実は…

このエントリで紹介した解決方法はSimpleCovのREADMEに書いてありました。
やはりドキュメントは時々ちゃんと読まないとダメだなと反省しました。

最後に

アクトインディでは計測しながらコードを改善していきたいエンジニアを募集しています。