0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

requestspecでlet!を使用する

Posted at

requestspecを使ってコントローラーテストを行っていたのですが、let!の使い方に関して不明点があったため、記録として残しておきます。
letを始めRSpecに関する基礎的な文法についてはこちらの記事を参考にさせていただきました。
使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」

##開発環境
rails (6.0.3.3)
rspec-rails (4.0.1)

疑問内容

####なぜletではなくlet!なのか。

destroy以外では:laundryに対してletで通ったのですがdestroyアクションの時だけテストが通らずlet!にするとテストが通ります。
挙動としては以下を想定しています。

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

コードは以下のとおりです。(該当箇所のみ抜粋しています。)

requestspec

describe '#destroy' do
    let(:admin_user) { FactoryBot.create(:user, admin: true) }
    let(:user) { FactoryBot.create(:user, admin: false) }
    #この部分がなぜlet!になるのか
    let!(:laundry) { FactoryBot.create(:laundry) }

    context '管理者の場合' do
      it '店舗情報を削除できること' do
        sign_in admin_user
        expect {
          delete laundry_path(laundry)
      }.to change { Laundry.count }.by(-1)
      end
    end

    context '認可されていないユーザーの場合' do
      it '店舗情報を削除できないこと' do
        sign_in user
        expect {
          delete laundry_path(laundry)
        }.to_not change { Laundry.count }
      end
    end
  end

##結論
###deleteアクションを実行する前にデータをセットしておくため。

処理実行の流れとしては以下のとおりです。
1.Laundry.countが実行される(処理実行前のデータの数を確認)
2.expect { ... }が実行される(削除の実行)
3.Laundry.countが実行される(削除実行後のデータの数を確認)

先ほどのコードと処理実行の流れを照らし合わせながらみていきます。

  • let(遅延評価)にした場合
   let(:admin_user) { FactoryBot.create(:user, admin: true) }
    let(:user) { FactoryBot.create(:user, admin: false) }
    let(:laundry) { FactoryBot.create(:laundry) }

   context '管理者の場合' do
      it '店舗情報を削除できること' do
        sign_in admin_user
        expect {
          delete laundry_path(laundry)
      }.to change { Laundry.count }.by(-1)
     end
    end

1.Laundry.countが実行される(処理実行前のデータの数を確認)
 → FactoryBot.create(:laundry)は実行されていないためLaundry.countは0
2.expect { ... }が実行される(削除の実行)
 → delete laundry_path(laundry)でデータは作成されるが削除されるためこちらもLaundry.countは0
3.Laundry.countが実行される(削除実行後のデータの数を確認)
 → 予想は1つデータが減っているはず(to change { Laundry.count }.by(-1))だが、上記1.2からデータ数に変化はないためエラーが発生する。

エラー内容

 expected `Laundry.count` to have changed by -1, but was changed by 0

削除できないことを確認したい場合も同様の流れでエラーが発生します。


it '店舗情報を削除できないこと' do
   sign_in user
   expect {
     delete laundry_path(laundry)
   }.to_not change { Laundry.count }
end

1.データは作成されていないためLaundry.countは0
2.expext{}で削除を行うが管理者ではないため削除は実行されない(Laundry.countは1)
3.to_not change { Laundry.count }でデータ数は変わっていないことを確認したいが0から1に変更しているためエラーが発生する。

expected `Laundry.count` not to have changed, but did change from 0 to 1

以上からletで遅延評価にすることによりエラーが発生してしまいます。
これをlet!にして事前評価(各itが実行される直前にデータを作成)にすることでLaundry.countが1からスタートし、テストがパスします。

##まとめ
createアクションの場合はデータが増えることを確認したいため前もってデータを用意する必要はないですが、destroyアクションの場合はデータが減ることを確認する必要があるためlet!を使う必要があります。

以上です。ありがとうございました!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?