morishitaです。
アクトインディではRailsアプリケーションのテストをRSpecで書いています。
そして、テストカバレッジをSimpleCovを使って計測しています。
何故かカバレッジが計測できないクラスがあったのですが、計測できるように解決した件を紹介します。
tl;dr
SimpleCov.start
は次のコードの前に実行する
require File.expand_path('../../config/environment', __FILE__)
- .simplecov ファイルに
SimpleCov.start
を実行するコードを書いてRSpecの起動するクリプトで最初にrequire 'simplecov'
したほうが楽。
改善前の状況
例えばいこレポではこんな感じでプルリクエストにPushするとテストが実行され、 その結果がカバレッジとともにSlackに通知される仕組みになっています1。
そこそこ高いカバレッジを維持できているのですが、 一部どうしても計測できないコードがあって悩んでいました。
User
モデルはログインユーザを表します。
なのでテストで使わないわけがないのですが 0.0 % となっています。
SimpleCov.start
の位置が重要
結論から言うと計測したいクラスがロードされるよりも先に SimpleCov.start
が実行される必要があります。
そんなつもりはないのに、SimpleCov.start
の前にロードしちゃっているありがちな例は次のケースです。
- FactoryBotのFactoryのStatic attributesでロードしてしまっている
- 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.rbをrequire
してやればいいのです。
test-queue
一方、アクトインディではCI環境だと test-queueを使ってRSpecを並列実行しています。
その場合、rspec
コマンドではなく、TestQueue::Runner::RSpec
を継承したクラスを実装して独自のRSpec起動スクリプトを用意して使います。そこでもハマっていました。
その起動スクリプト内でも次のコードが存在していました。
require File.expand_path('../../config/environment', __FILE__)
特に test-queue
で SimpleCov.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'
してやればいいだけなので、この方法で設定したほうがシンプルでいいと思います。
改善した結果
さて、こうして改善した結果を確認してみましょう。
お、カバレッジが少し大きくなりました。
で、問題のUserクラスはというと…。
やりました! 計測できています!
実は…
このエントリで紹介した解決方法はSimpleCovのREADMEに書いてありました。
やはりドキュメントは時々ちゃんと読まないとダメだなと反省しました。
最後に
アクトインディでは計測しながらコードを改善していきたいエンジニアを募集しています。