Upgrade to Pro — share decks privately, control downloads, hide ads and more …

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

ham
November 13, 2019

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

ham

November 13, 2019
Tweet

More Decks by ham

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  5. よくないところを直す

    View Slide

  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にした。

    View Slide

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

    View Slide

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

    View Slide

  9. database cleanerを外す

    View Slide

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

    View Slide

  11. profileで解析

    View Slide

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

    View Slide

  13. create_list撲滅

    View Slide

  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

    View Slide

  15. モデル生成のN+1対応

    View Slide

  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

    View Slide

  17. sleepをスタブ化

    View Slide

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

    View Slide

  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

    View Slide

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

    View Slide