6時だョ!!全員集合!!

Rails・JavaScrictを中心にアウトプットします。

2021年10月4日 現場Rails Chapter5-9 他のユーザーが作成したタスクが表示されないことの確認

前回はユーザーAが作成したタスクがページに表示されていることを確認するテストを書きました。 今回はユーザーBがログインした際にユーザーAのタスクは表示されていないことを確認するテストを書いていきます。

require 'rails_helper'

describe 'タスク管理機能', type: :system do
  describe '一覧表示機能' do
    before do
    # user_aを名前とメールを指定して、新たに作成する
    user_a = FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com')
    # user_aに紐づくタスクをタスク名を指定して作成する
    FactoryBot.create(:task, name: '最初のタスク', user: user_a)
    end
    
    context 'ユーザーAがログインしているとき' do
      before do
        # ログインページへアクセスする
        visit login_path
        # メールアドレスとパスワードをログインフォームに入力する
        fill_in 'メールアドレス', with: 'a@example.com'
        fill_in 'パスワード', with: 'password'
        # ログインボタンを押す
        click_button 'ログインする'
      end
      
      it 'ユーザーAが作成したタスクが表示される' do
        # pageに最初のタスクという内容があることを期待する。
        expect(page).to have_content '最初のタスク'
      end
   ーーーーーーーーーーーーーーーーーーーーーーー 
     # ここからが今回学んだ内容になります。
     context 'ユーザーBがログインしているとき' do
       before do
         Factorybot.create(:task, name: 'ユーザーB', email: 'a@example.com', user: user_a)
         visit login_path
         fill_in 'メールアドレス', with: login_user.email
         fill_in 'パスワード', with: login_user.password
         # ログインボタンを押す
         click_button 'ログイン'
       end
       
       context 'ユーザーAがログインしているとき' do
        let(:login_user){ user_a }
        
        it 'ユーザーAが作成したタスクが表示される'
          expect(page).to have_content '最初のタスク'
        end
        
      end
        
      # ユーザーBとしてログインしている為、ユーザーAが作成した最初のタスクが表示されないようにする
      it 'ユーザーAが作成したタスクが表示されない' do
       # '最初のタスク'が表示されない。
       expect(page).to have_no_content '最初のタスク'
      end
    end
  end
end

beforeを利用した共通化

コード自体の共通点が多い時、似たコードをまとめて、共通化することができます。今回は、beforeを使って共通化していきます。

describe 'タスク管理機能', type: :system do
   describe '一覧表示機能' do
     let(:user_a){ FactoryBot.create(:user, name: 'ユーザーA', email: 'a@example.com')}
     let(:user_b){ FactoryBot.create(:user, name: 'ユーザーB', email: 'b@example.com')}
     
     before do
       FactoryBot.create(:task, name: '最初のタスク', user: user_a)
       # ここでletのユーザーAが実際にデータベースに登録されます。
       visit login_path
       fill_in 'メールアドレス', with: login_user.email
       fill_in 'パスワード', with: login_user.password
       click_button 'ログインする'
     end
     
     context 'ユーザーAがログインしている時' do
       let(:login_user) { user_a }
       
       it 'ユーザーAが作成したタスクが表示される' do
         expect(page).to have_content '最初のタスク'
       end
     end
     
     context 'ユーザーBがログインしている時' do
       let(:login_user) { user_b }
       
       it 'ユーザーAが作成したタスクが表示されない' do
         expect(page).to have_no_content '最初のタスク'
       end
     end  
   end
end

否定系の書き方

以下の3種類がありますがどれを使用しても大丈夫です。

expect(page).to have_no_content '最初のタスク'
expect(page).not_to have_content '最初のタスク'
expect(page).to_not have_content '最初のタスク'

伊藤淳一さんはこちらのツイートで to_notが書きやすいとおっしゃっているので参考にしてみても良いかも知れません。


上記のコード(否定系の書き方の上)のように、同じ階層にあるすべてのcontext~end内で共通する処理は、一つ上の階層内で、beforeを定義して、共通処理を書くことが出来ます。
この場合は、context処理前にbeforeブロック内の処理が実行されます。ここで実行される処理は、パスワードやメールなどの値は実際に入っておらず、箱のような状態になっています。実際にcontext処理内のletで、before処理内のパスワードやメールの値が入ります。

letとlet!

現場Rails P.213~P.214に詳しく記載されてるので、以下に引用します。

letは呼び出されたタイミングで実行され、一度も呼び出されないときは実行されずじまいとなります。
そのため、例えばlogin_userというletを定義したとしても、以下の例のようにlogin_userを一度も呼び出さずにログインすると、ユーザーが作られることがありません。そのため、ログインが失敗してしまいます。

describe '一覧表示機能' do
  let(:login_user) { FactoryBot.create(:user, name: 'ユーザーA, email: 'a@exaple.com)}  
  
  before do  
    # let(:login_user)の定義を一度も呼び出さずにログインする
    visit login_path
    fill_in 'メールアドレス', with: 'a@example.com'
    fill_in 'パスワード', with: 'password'
    click_button 'ログインする' #=> ユーザーAが作られていないためにログイン失敗する
  end
  ...
end

このような場合は、letの代わりにlet!を利用すれば、beforeの前にユーザーが登録されるため、意図通りにログインできるようになります(参照しないのであればlet!にもせずにbeforeに記述しても良いという考え方もできますが、let!を使う方が読みやすくなる場合があります。また「呼び出されるケースと呼び出されないケースがあるが、データは常に作りたい」といった場合にも便利に利用できます)

参照

現場Rails(p.205~214)

RSpecで「~ではないこと」を検証するときは expect(x).to_not 、または expect(x).not_to のどちらを使うべきか?

使えるRSpec入門・その1「RSpecの基本的な構文や便利な機能を理解する」

RSpecのletを使うのはどんなときか?(翻訳)

【Rspec】ややこしいletの挙動についてまとめてみる。