RSpecの実行時間を1/5にした話

9cfde65b598c5e2fe8578307bfbad682?s=47 ham
November 13, 2019

 RSpecの実行時間を1/5にした話

9cfde65b598c5e2fe8578307bfbad682?s=128

ham

November 13, 2019
Tweet

Transcript

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

  2. 自己紹介 name: naoto hamada twitter: @hamchance0215 biography: → → 最近はRoRのBackend

    Engineer Javascriptの知識が古すぎるのでUpdate中
  3. アプリ概要 概要 WebアプリケーションのAPI versions Ruby 2.4.1 Rails 5.2.3 Rspec-rails 3.9.0

    エンドポイント数 79 ジョブ数 27
  4. 改善前 $ bundle exec rspec ……………*………………………………………… ……………*…………*……………………………… …………………………………………**…… Finished in

    5 minutes 57 seconds 1366 examples, 0 failures, 5 pending
  5. よくないところを直す

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

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

  9. database cleanerを外す

  10. database cleanerを外す database cleanerを外した理由 • RSpecがテストごとにRollbackしてくれる • いろんなところに設定が増殖していた • ar_internal_metadataも消しちゃう

    • truncateのスピードが遅い 詳細は下記にまとめています https://qiita.com/ham0215/items/7516117df87d2631e31d
  11. profileで解析

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

  13. create_list撲滅

  14. 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
  15. モデル生成のN+1対応

  16. モデル生成の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
  17. sleepをスタブ化

  18. 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を定義

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