Rspecのシステムテストで実行時間が30分以上かかるテストがありました。example数は約100件です。
何とかならないものかと考えた結果、aggregate_failures
を導入することにしました。これによりテスト実行時間を約10分まで短縮することができました。
aggregate_failuresとは?
it内であるexpectが失敗した時に、次のitに移行せず、it内の全てのexpectを実行してくれるメソッドです。通常であれば1つのexpectが失敗した時点で次のitに移行してしまいます。aggregate_failures
は全てのexpectを実行して、失敗したexpectが複数件あればそれらの情報を全て出力してくれます。
どんなテストだったか?
こんなテストでした。
beforeを使っていて、itごとにFactoryBotでuserをbuild→ログイン処理をしています。
describe 'XXX', type: :system, js: true do
before(:each) { @user = FactoryBot.build(:user, email: 'xxxx.com') }
subject { @user }
context "YYY" do
before do
# ログイン処理
end
end
it 'hoge1' do
expect(zzz1).to eq zzz1
end
it 'hoge2' do
expect(zzz2).to eq zzz2
end
it 'hoge3' do
expect(zzz3).to eq zzz3
end
.
.
.
it 'hogeN' do
expect(zzzN).to eq zzzN
end
end
遅くなっている原因はどこか?
①FactoryBot.buildの部分
FactoryBotでアソシエーションを指定しているとbuildでもレコードが作成され、その分遅くなるらしいです。しかし今回はアソシエーションの指定はしていないのでここは原因ではないと考えました。
②ログイン処理の部分
今回のケースでは明らかにここでした。毎itごとにログイン処理をしているのでめちゃくちゃ時間がかかっていました。
before(:context)
解決策としてbefore(:context)
を使用し、contextごとにログイン処理を共通化しようと思いました。
しかしここで問題がありました。
ログイン処理の中にCapybara.current_session.server.port
でポート番号を取得する場面があるのですが、これがnilになってしまうのです。
before(:context)
の時点ではサーバーが起動していないことが理由と思われます。
before(:context)
によるログイン処理共通化は断念しました。
aggregate_failuresを利用する
そこで出てきたのがaggregate_failures
です。
前述の通りですがitの中にまとめてexpectを記述することができるので、itの数を大幅の削減できます。
itの数が大幅に削減されるので、before doでログイン処理が実行される回数も大幅に減るのです。
aggregate_failuresの導入方法
spec/spec_helper.rbに以下のように追記するだけで全てのexpectに適用されます。
RSpec.configure do |config|
config.define_derived_metadata do |meta|
meta[:aggregate_failures] = true unless meta.key?(:aggregate_failures)
end
end
導入結果
aggregate_failuresを導入した結果このようにexpectをまとめることができテスト実行時間は10分程度になりました!
describe 'XXX', type: :system, js: true do
before(:each) { @user = FactoryBot.build(:user, email: 'xxxx.com') }
subject { @user }
context "YYY" do
before do
# ログイン処理
end
end
it 'hoge1' do
expect(zzz1).to eq zzz1
expect(zzz2).to eq zzz2
expect(zzz3).to eq zzz3
.
.
.
expect(zzzN).to eq zzzN
end
end
終わりに
テスト実行時間は削減できたのですが、おそらくブラウザの動作が重くSeleniumがDOMを見つけられず適切なボタンをクリックできないことが原因で、テストがランダムで落ちる問題が発生しました。
こちらの解決策についても後日記事を書く予定です。