📚
【フロントエンドが初めて触る Rails 】Rspecでのletを使った遅延評価とは
概要
業務でRSpecを使ってテストを実装している際に、let!(:hoge)でモデルのインスタンス生成処理を書けばいいんだな、と思いながら実装していました。すると、先輩エンジニアから「letとlet!には遅延評価の違いがある」との助言をもらいました。
その時は、「どんな場合に使い分ければいいんだ?」とすぐにはピンとこなかったので、今回しっかり調べて知識として整理してみようと思います。
letとlet!
-
let
:実際に参照される時に評価されインスタンスが生成される。- 遅延評価
-
let!
:テストが実行される前にすぐに評価されインスタンス生成される。- 即時評価
let
let
で書いておくと必要な時にのみ評価されるため、不要な計算が発生しなかったり、同じテスト内ではキャッシュされた値が使われことでパフォーマンス的に良いとのこと。
RSpec.describe 'letの遅延評価' do
let(:value) { puts "値を計算中"; 42 }
it 'test1' do
# 参照されるタイミングで初めて評価され生成される
expect(value).to eq(42)
# 同じテスト内では、ブロックは再実行されずキャッシュされた値が使われる
expect(value).to eq(42)
end
it 'test2' do
# このタイミングでもう一度評価され生成される
expect(value).to eq(42)
end
end
let!
逆にlet!
ではテストが実行する前にインスタンスが生成されるため、そのテストブロックが終わるまで保持し続けるため、パフォーマン的には遅延評価よりは下がるとのこと。
ただ、調べていくと、let!
じゃないと意図したテストにならない場合があるということがわかった。
let!を使うべき場面
下記のテストでは、let
で定義したbook
が評価されるタイミングではまだ生成されていないためエラーになる。
そのため、パフォーマンスだけを考え、let
を使うとテストでつまづくことも多くありそう。
# models/author.rb
class Author < ApplicationRecord
has_many :books
end
# models/book.rb
class Book < ApplicationRecord
belongs_to :author
end
let(:author) { FactoryBot.create(:author) }
let(:book) { FactoryBot.create(:book, author: author, title: 'hoge') }
it 'test3' do
expect(author.books.first.title).to eq 'hoge'
end
まとめ
今回はlet
とlet!
の違いをまとめてみました。
安定してテストを実行できることを重要視すると、パフォーマンスにシビアにならない限りは基本的にはlet!
が推奨かもしれません。
遅延評価はパフォーマンス的には良いかもしれませんが、使い所に少し気をつけるべきだなと感じました。
参考
Discussion