letとlet!の呼び出し方の違い
let と let!
Use
let
to define a memolized helper method. The value will be cached across multiple calls in the same example but not across examples.
letで定義したメソッドは、同じdescribeの中であれば何度でも呼び出すことが出きる。
Note that
let
is lazy-evaluated: it is not evaluated until the first time the method it defines is invoked.
letは遅延評価を採用している。なので、letで定義したメソッドは、そのメソッドが最初に呼び出されるまでは評価されることがない。
describe 'let' do
let(:user) { create(:user) }
let(:user_article) { create(:article, user_id: user.id) }
specify 'User が Article を持っていること' do
expect(user.articles.first).to eq user_article
end
end
上記のような場合、user.articles.firstを呼び出した時点では、create(:user) は実行されていない為、下記のようなFailure/Errorを吐き出す。
Failure/Error: expect(user.articles.first).to eq user_article
expect[].to eq user_article
//expectの中身がありませんというエラーが出てしまう。
全てのメソッドの前で実行させるためには、感嘆詞をつけてlet!にしてあげる。
describe 'let' do
let!(:user) { create(:user) }
let!(:user_article) { create(:article, user_id: user.id) }
specify 'User が Article を持っていること' do
expect(user.articles.first).to eq user_article
end
end
こうすることで、userインスタンスとarticleインスタンスが expect(user.articles.first)とuser_articleの前で呼び出されるので、テストをパスさせることが出来る。
create(:user)
create(:article, user_id: user.id)
引数を複数使うこともできる
letの中身は、引数を複数設定することができる。
例えば以下の場合、spec/factories/article.rbにおいてtraitを用いて定義した、pastとwith_sentenceをpast_article_with_sentenceという別名をつけて定義している。
spec/factories/article.rb
FactoryBot.define do
factory :article do
sequence(:title) { |n| "title-#{n}" }
sequence(:slug) { |n| "slug-#{n}" }
category
end
trait :past do
published_at { DateTime.now.ago(1.hours) }
state { :published }
end
trait :with_sentence do
transient do
sequence(:sentence_body) { |n| "test_body_#{n}" }
end
after(:build) do |article, evaluator|
article.sentences << create(:sentence, body: evaluator.sentence_body)
end
end
end
aritcle.rb
describe "検索機能" do
let(:past_article_with_sentence) { create(:article, :past, :with_sentence, sentence_body: 'こんにちは') }
it do
visit edit_admin_article_path(past_article_with_another_sentence.uuid)
end