test-profを試していたら、let!の挙動を勘違いしていることに気がついた
これは何
test-profというGemを試してみた。let!の挙動を勘違いしていることに気がついた。
test-profとは
テストのパフォーマンスを計測できるGem。
- GitHub: test-prof/test-prof: Ruby Tests Profiling Toolbox
- Doc: TestProf: Ruby tests profiling and optimization toolbox
テストからのフィードバックの速さは開発者の生産性に影響するので、パフォーマンス改善は重要。DiscourseやDev.toなどのOSSで活用されている。
Install
group :test do
gem "test-prof", "~> 1.0"
endテストパフォーマンスを計測
EVENT_PROF, FPROF 変数を渡すと、factory-botでどれくらいデータを生成しているかを計測し、結果を表示してくれる。
- Event Profiler - TestProf: Ruby tests profiling and optimization toolbox
- Factory Profiler - TestProf: Ruby tests profiling and optimization toolbox
require 'rails_helper'
RSpec.describe Article, type: :model do
let!(:articles) { create_list(:article, 10) }
it { expect(true).to eq true }
it { expect(true).to eq true }
it { expect(true).to eq true }
it { expect(true).to eq true }
endEVENT_PROF=factory.create FPROF=1 bundle exec rspec /spec/models/article_spec.rb
[TEST PROF INFO] Factories usage
Total: 40
Total top-level: 40
Total time: 00:00.137 (out of 00:00.552)
Total uniq factories: 1
total top-level total time time per call top-level time name
40 40 0.1377s 0.0034s 0.1377s articlelet!の挙動を勘違いしていた
上記を走らせたときarticlesは最初だけ評価されて、Factoryの生成は10では?と最初思っていた。
idとcountを出してみる。
it do
expect(true).to eq true
puts "count: #{Article.count}"
puts Article.pluck(:id)
end
it do
expect(true).to eq true
puts "count: #{Article.count}"
puts Article.pluck(:id)
endbundle exec rspec /spec/models/article_spec.rb
count: 10
261
262
...
270
is expected to eq true
count: 10
271
272
...
280自分がlet!の挙動を勘違いしていたことに気づいた。
let!はすべてのexampleの前で呼び出される。RubyMineを使っていれば、RSpec logからも挙動を確認できる。
なので、共通で使うデータをlet!で定義して、その下で context や it を書きまくるとINSERTが何度も走るので、テストのパフォーマンスが落ちる。
パフォーマンスを改善するには
共通で使うデータを一度だけINSERTされるようにするには、どうすれば良いか。
結論、test-profが提供しているbefore_allが良さそうな気がしている。
rails_helper.rb
require "test_prof/recipes/rspec/before_all"before_all do
create_list(:article, 10)
endちなみに、rspecがbefore(:all)というのを提供しているが、これはDBにデータが残り続けるので厄介そうなので使いたくない。