今回に5つのRspecの速度改善方法をご紹介したいと思います。
1. Parallel Testing
2. Adding gem test_prof
3. Optimize Setup Time
4. Using stubs
and mock
5. Using trait
in Factory
1. Parallel Testing
parallel_testsとは
- マルチコアCPUで並列実行するテスト用のGem
parallel_testsを導入する方法
Gemのホームページに参考してください。
Note
- CPUのRamをよく食い尽くすので実行する間に他の作業を離してください。w
2. Adding gem test_prof
問題
# bad
it { is_expected.to be_success }
it { is_expected.to have_header("X-TOTAL-PAGES", 10) }
it { is_expected.to have_header("X-NEXT-PAGE", 2) }
its(:status) { is_expected.to eq(200) }
# good
it "returns the second page", :aggregate_failures do
is_expected.to be_success
is_expected.to have_header("X-TOTAL-PAGES", 10)
is_expected.to have_header("X-NEXT-PAGE", 2)
expect(subject.status).to eq(200)
end
- itを呼ぶと、もう一回subjectを呼ぶので、時間が遅くなっています
→ itを一回だけ呼ぶようにした方が良いと思う
変更方法
rubocop -r 'test_prof/rubocop' --only RSpec/AggregateFailures -a spec/
Rspecを確認した結果
rspec spec/api/gmo/transaction_api_spec.rb
BEFORE:
Finished in 7.53 seconds (files took 7.74 seconds to load)
AFTER:
Finished in 2.08 seconds (files took 7.39 seconds to load)
- 1ファイルだけ確認したが、早くなってきた!
- 皆さんもテスト書く時、注意してくださいね!
3. Optimize Setup Time
BEFORE:
- テストのプロセスに変更されないデーターがあったら、
let
の代わりにbefore(:all)
ブロックにインスタンス変数に移動した方が設定時間減らすできると思います。
context "a" do
...
let(:shop){create :shop}
...
end
context "b" do
...
let(:shop){create :shop}
...
end
context "c" do
...
let(:shop){create :shop}
...
end
# → テストケースによって`shop`を作る
AFTER:
before(:all){@shop = create :shop}
context "a" do
# @shopを使える
end
context "b" do
# @shopを使える
end
context "c" do
# @shopを使える
end
# → `shop`を一回だけ作る
4. Using stubs
and mock
create
, build
, build_stubbed
の違い
- 多分他のTopicに説明が多いと思うですが、簡単に言うと
■ create: 対象のModelと関係あるModelがDBに作成される
■ build: 対象のModelではなく、関係あるModelしかがDBに作成されない
■ build_stubbed: 対象のModelも、関係あるModelも何もDBに作成されないが、
ちゃんと本物のModelとして使える
-
build_stubbed
が上の3つの中に一番早いなので、DBにあまり関係ないとか、Modelの関係が必要ないテストケースなどに使った方が速度は早くなります。 - できるだけ、この順番で使えばもっと早くなると思う
double → build_stubbed → build → create
- build_stubbedの場合、DBに保存やアソシエーション先をDBに保存できませんので、ご注意ください。
Using allow
bad.rb
context "a" do
before do
form.available_from_kind_is_use = nil
form.available_from_kind_backend = nil
form.available_from_kind_frontend = nil
form.save!
end
end
good.rb
context "a" do
before do
allow(form).to receive(:available_from_kind_is_use).and_return nil
allow(form).to receive(:available_from_kind_backend).and_return nil
allow(form).to receive(:available_from_kind_frontend).and_return nil
end
end
- DBを変更必要はない場合、
allow
を使った方が良いです
5. Using trait
in Factory
BEFORE
users.rb
FactoryGirl.define do
factory :user do
name "Hung"
location build(:location)
end
end
user_spec.rb
it do
user = build :user, name: "Hung"
expect(user.name).to eq("Hung")
end
- 上のケースだと、
location
が必要ないけど、もう作られた
AFTER
FactoryGirl.define do
factory :user do
name "Hung"
trait :with_location do
location build(:location)
end
end
end
-
location
が必要なテストケースだけに:with_location
使った方が良いと思います