Slide 1

Slide 1 text

RSpecの実行時間を1/5にした話 2019/11/13 Ebisu.rb #26 ham

Slide 2

Slide 2 text

自己紹介 name: naoto hamada twitter: @hamchance0215 biography: → → 最近はRoRのBackend Engineer Javascriptの知識が古すぎるのでUpdate中

Slide 3

Slide 3 text

アプリ概要 概要 WebアプリケーションのAPI versions Ruby 2.4.1 Rails 5.2.3 Rspec-rails 3.9.0 エンドポイント数 79 ジョブ数 27

Slide 4

Slide 4 text

改善前 $ bundle exec rspec ……………*………………………………………… ……………*…………*……………………………… …………………………………………**…… Finished in 5 minutes 57 seconds 1366 examples, 0 failures, 5 pending

Slide 5

Slide 5 text

よくないところを直す

Slide 6

Slide 6 text

jobを同期処理にする 下記の記述が個別のテストケースごとに書いてあった。 Rails.application.config.active_job.queue_adapter = :test 個別に記載していると冗長だし漏れることもあるので、 config/environments/test.rbに移動 発生した問題 active storageにファイルを上げた時にファイルの解析が非同期で動いてお り、それも同期処理になってしまいテストが遅くなった。 https://github.com/rails/rails/blob/master/activestorage/app/models/active_storage/blob/analyzable.rb#L37 →テストする必要ないのでmockにした。

Slide 7

Slide 7 text

外部接続のmockを整理 外部接続のmockがテストケースごとに書いてあった。 個別に記載していると冗長だし漏れることもあるので、 spec/spec_helper.rbに移動 config.before(:each) do allow(xxx).to receive(:yyy).and_return(true) ... end

Slide 8

Slide 8 text

Pending exampleを動かす(or 消す) pendingしているテストは意味がないので動くように直す。 本当に必要がないテストならば消す。 Pendingは極力使わない!!使ったらとしてもすぐ直す!!!!

Slide 9

Slide 9 text

database cleanerを外す

Slide 10

Slide 10 text

database cleanerを外す database cleanerを外した理由 ● RSpecがテストごとにRollbackしてくれる ● いろんなところに設定が増殖していた ● ar_internal_metadataも消しちゃう ● truncateのスピードが遅い 詳細は下記にまとめています https://qiita.com/ham0215/items/7516117df87d2631e31d

Slide 11

Slide 11 text

profileで解析

Slide 12

Slide 12 text

遅いテストを探す rspecに--profileをつけると遅いテストをランキング形式で並べてくれます これで遅いテストを探しました $ bundle exec rspec --profile 10

Slide 13

Slide 13 text

create_list撲滅

Slide 14

Slide 14 text

create_list撲滅 FactoryBot.create_listは大量データを作る時に便利ですが1件ごとに insertを発行してしまいます FactoryBot.create_list(:user, 100) →100件insertが発行される users = FactoryBot.build_list(:user, 100) User.import users →bulk insertで登録 詳細は下記にまとめています https://qiita.com/ham0215/items/dad6f5d3d63bd498d999

Slide 15

Slide 15 text

モデル生成のN+1対応

Slide 16

Slide 16 text

モデル生成のN+1対応 下記のように記述した時、review生成時にuserを毎回selectしてしまう。 let!(:user) { FactoryBot.create(:user) } # => insert user let!(:draft_review) { FactoryBot.create(:review, :draft, user_id: user.id) } # => select user + insert review let!(:published_review) { FactoryBot.create(:review, :published, user_id: user.id) } # => select user + insert review 下記のようにモデルを直接渡せばselectされない let!(:draft_review) { FactoryBot.create(:review, :draft, user: user) } # => insert review 詳細は下記にまとめています https://qiita.com/ham0215/items/2943511a4336a77a6aed

Slide 17

Slide 17 text

sleepをスタブ化

Slide 18

Slide 18 text

sleepをスタブ化 外部接続のリトライなどで明示的にsleepしている処理があるが、テストでは sleepしなくて良い場合が多い →sleepをmockにする 発生した問題 spec/spec_helper.rbにsleepのmock定義したら、 RSpec::Mocks::OutsideOfExampleErrorが発生するようになった。 DBコネクションのsleepもmockになってしまったようだ https://github.com/rails/rails/blob/6-0-stable/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb#L339 →テストケースごとに個別にmockを定義

Slide 19

Slide 19 text

改善後 $ bundle exec rspec ……………………………………………………… ……………………………………………………… ………………………………………………..… Finished in 1 minute 45.64 seconds 1366 examples, 0 failures 5 minute 58 seconds ↓ 約1/5に短縮!! 1 minute 45.64 seconds

Slide 20

Slide 20 text

まとめ テストコードはパフォーマンスを気にせず実装されていることが多く、ちょっと 改善するだけで効果絶大です!!(と個人的には思っています) 運用改善の時間が取れる場合はぜひ見直してみてはいかがでしょうか!?