質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

Q&A

解決済

1回答

1426閲覧

letとlet!の挙動の違いがわかりません(requestspecでエラーが発生する)

退会済みユーザー

退会済みユーザー

総合スコア0

Ruby on Rails 6

Ruby on Rails 6は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

RSpec

RSpecはRuby用のBDD(behaviour-driven development)フレームワークです。

0グッド

1クリップ

投稿2020/11/23 12:42

##現状
現在、Railsでアプリケーションを作成しています。
RSpecのrequestsスペックにてコントローラーのテストを行っているのですが、destroyアクションのテストで躓いているため、わかる方いましたら教えてください。

挙動としては以下を想定しています。

  • 管理者(admin: true) → 削除が行える
  • 管理者以外(admin: false) → 削除は行われずトップページにリダイレクトする

以下のコードでテストを行ったのですが、削除が思ったように行われません。(なぜか削除したい時に削除されず、削除して欲しくない時に削除されるという結果が返ってきています。)
トップページへのリダイレクトに関するテストは通ります。

requestspec

1describe '#destroy' do 2 let(:admin_user) { FactoryBot.create(:user, admin: true) } 3 let(:user) { FactoryBot.create(:user, admin: false) } 4 let(:laundry) { FactoryBot.create(:laundry) } 5 6 context '管理者の場合' do 7 it '店舗情報を削除できること' do 8 sign_in admin_user 9 expect { 10 delete laundry_path(laundry) 11 }.to change { Laundry.count }.by(-1) 12 end 13 end 14 15 context '認可されていないユーザーの場合' do 16 it '店舗情報を削除できないこと' do 17 sign_in user 18 expect { 19 delete laundry_path(laundry) 20 }.to_not change { Laundry.count } 21 end 22 23 it 'トップページにリダイレクトすること' do 24 sign_in user 25 delete laundry_path(laundry) 26 expect(response).to redirect_to root_path 27 end 28 end

エラー文↓

1) Laundries #destroy 管理者の場合 店舗情報を削除できること Failure/Error: expect { delete laundry_path(laundry) }.to change { Laundry.count }.by(-1) expected `Laundry.count` to have changed by -1, but was changed by 0 # ./spec/requests/laundries_spec.rb:226:in `block (4 levels) in <top (required)>' 2) Laundries #destroy 認可されていないユーザーの場合 店舗情報を削除できないこと Failure/Error: expect { delete laundry_path(laundry) }.to_not change { Laundry.count } expected `Laundry.count` not to have changed, but did change from 0 to 1 # ./spec/requests/laundries_spec.rb:240:in `block (4 levels) in <top (required)>'

controller

1 2 before_action :check_user, except: [:index, :show, :search] 3 4 def destroy 5 laundry = Laundry.find(params[:id]) 6 if laundry.destroy 7 redirect_to root_path, notice: '削除が完了しました' 8 else 9 redirect_to root_path, alert: '削除が失敗しました' 10 end 11 end 12 13private 14 15 def check_user 16 redirect_to root_path unless user_signed_in? && current_user.admin? 17 end

##質問内容
let(:laundry) { FactoryBot.create(:laundry) }let!(:laundry) { FactoryBot.create(:laundry) }にするとテストが成功するのですが、なぜlet!にすることで成功するのかがわかりません。

そもそも自分が書いたテストコード自体が間違っているのでしょうか?
わかる方いましたら教えていただきたいです。

よろしくお願いいたします。

##開発環境

  • rails (6.0.3.3)
  • ruby (2.6.5)
  • rspec-rails (4.0.1)

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

letは遅延評価、let!は事前評価です。

ruby

1let(:laundry){ FactoryBot.create(:laundry) } 2it do 3 expect { 4 delete laundry_path(laundry) 5 }.to change { Laundry.count } 6end 7

上記のコードは

  1. Laundry.countが実行される
  2. expect { ... }が実行される
  3. Laundry.countが実行される

という流れです。

1 の時点でLaundry.count0
2 でlaundryが遅延評価され作成されるが、削除される
3 でLaundry.count0

let! にすると最初のLaundry.count が 1 なので、1 が(削除されて) 0 になります。

ruby

1let(:laundry){ FactoryBot.create(:laundry) } 2it do 3 expect { 4 p "expect" 5 delete laundry_path(laundry) 6 }.to change { p "change"; Laundry.count } 7end

としてテストを走らせると
"change" "expect" "change" と表示されるはずです。

投稿2020/11/23 20:17

neko_daisuki

総合スコア2090

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2020/11/23 23:49

1の「Laundry.countが実行される」の部分の考えが抜けていました。 ありがとうございます。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問