0
0

More than 1 year has passed since last update.

aggregate_failuresを使ってRspecのシステムテストを高速化した

Posted at

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を見つけられず適切なボタンをクリックできないことが原因で、テストがランダムで落ちる問題が発生しました。
こちらの解決策についても後日記事を書く予定です。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0