📘 hkob-astro-notion-blog

これまではてなブログにて情報発信をしていましたが、令和5年3月22日より、こちらでの情報発信を始めました。2019年以前の古い記事は過去の Middleman 時代のものなので、情報が古いです。記録のためだけに残しています。

kurasus_controller の request spec(3) - 不定期刊 Rails App を作る(18)

💡
この記事は Middleman 時代に書いた古いものです。記録のため、astro-notion-blog に移行していますが、あまり参考にしないでください。
request spec による edit のテストと実装
  1. GET #edit の spec を spec/requests/kurasus_spec.rb に記載する. edit では :id で指定された一つのクラスを取得する. 何も対処しないと,URL の :id を偽装することで,他人のクラスを編集できてしまうことになる. そこで,自分の所有しているクラスの場合と,所有していないクラスの場合でテストを二つ用意する.
    # テストの最初に追加する.自分が所有しないクラスを let で遅延取得できるようにしておく.
        let(:not_mine) { kurasu_factory :taro3300 }
        (中略)
        describe 'GET #edit' do
          context 'owned object' do
            subject { -> { get edit_kurasu_path(object) } }
            it_behaves_like :response_status_check, 200
            it_behaves_like :response_body_includes, '2300'
          end
    
          context 'not owned object' do
            subject { -> { get edit_kurasu_path(not_mine) } }
            it_behaves_like :response_status_check, 302
            it_behaves_like :redirect_to
            it_behaves_like :flash_alert_message, 'クラスを編集する権限がありません.'
          end
        end
  2. GET #edit の実装を app/controllers/kurasus_controller.rb に記載する. 今後,update, destroy, show などでも利用するので,get_one というクラスを一つ取得するプライベートメソッドを用意しておく. このため,edit, update, destroy, show の時だけ,get_one を before_action するように設定している. この get_one メソッドでは,current_teacher.kurasus でフィルタされているため,他人のクラスのものは取得できず nil が返ってくるはずである. そこで,edit では @kurasu が nil の時に redirect_to で kurasus_path に飛ばしている. この時,edit の render を描画して欲しくないため,and return を追加している(redirect_to が true を返すので,自動的に and の後ろの return が実行される).
    # 先頭に追加
      before_action :get_one, only: %i[edit update destroy show]
    (中略)
    
      def edit
        redirect_to(kurasus_path(edit_mode: true), alert: alert_message(Kurasu)) and return if @kurasu.nil?
      end
    (中略)
      def get_one
        @kurasu = current_teacher.kurasus.find_by_id(params[:id])
      end
      private :get_one
  3. GET #edit のビューを app/views/kurasus/edit.html.haml に記載する. locale で差異を 吸収しているため,new.html.haml と edit.html.haml は同一内容である.そのためコピー(またはハードリンク)すればよい.
    cp app/views/kurasus/{new,edit}.html.haml
request spec による update のテストと実装
  1. PATCH #update の spec を spec/requests/kurasus_spec.rb に記載する. 更新に成功する場合,失敗する場合,他人のクラスを取得した場合の三種類を確認できればよい. 利用した shared_example は以下の通りである(追加分).
    • change_object_value_by_update: モデルの属性が変化したか確認
    • not_change_object_value_by_update: モデルが属性が変化していないことを確認
    describe 'PATCH #update' do
      before { @attrs = object.attributes }
    
      context 'owned object' do
        subject { -> { patch kurasu_path(object), params: {kurasu: @attrs} } }
    
        context '正しいパラメータに対して' do
          before { @attrs['name'] = 'new name' }
          it_behaves_like :response_status_check, 302
          it_behaves_like :change_object_value_by_update, Kurasu, :name, 'new name'
          it_behaves_like :redirect_to
          it_behaves_like :flash_notice_message, 'クラスを更新しました.'
        end
    
        context '不正なパラメータに対して' do
          before { @attrs['name'] = nil }
          it_behaves_like :response_status_check, 200
          it_behaves_like :not_change_object_value_by_update, Kurasu, :name
          it_behaves_like :flash_alert_message, 'クラスが更新できません.'
        end
      end
    
      context 'not owned object' do
        subject { -> { put kurasu_path(not_mine), params: {kurasu: @attrs} } }
        it_behaves_like :response_status_check, 302
        it_behaves_like :redirect_to
        it_behaves_like :flash_alert_message, 'クラスが更新できません.'
      end
    end
  2. PATCH #update の実装を app/controllers/kurasus_controller.rb に記載する.
    def update
      redirect_to(kurasus_path, alert: alert_message(Kurasu)) and return if @kurasu.nil?
      if @kurasu.update_attributes(kurasu_params)
        redirect_to kurasus_path(edit_mode: true), notice: notice_message(Kurasu)
      else
        flash.now[:alert] = alert_message(Kurasu)
        render action: :edit
      end
    end
  3. 最終的な guard 出力はこんな感じになった(本日分のみ).全てテストが通過している.
    GET #edit
      owned object
        behaves like response_status_check
          response should be 200
        behaves like response_body_includes
          response body includes 2300
      not owned object
        behaves like response_status_check
          response should be 302
        behaves like redirect_to
          should redirect to "/kurasus"
        behaves like flash_alert_message
          should eq "クラスを編集する権限がありません."
    PATCH #update
      owned object
        正しいパラメータに対して
          behaves like response_status_check
            response should be 302
          behaves like change_object_value_by_update
            should change `klass.find(object.id).send(key)` from "2300" to "new name"
          behaves like redirect_to
            should redirect to "/kurasus?edit_mode=true"
          behaves like flash_notice_message
            should eq "クラスを更新しました."
        不正なパラメータに対して
          behaves like response_status_check
            response should be 200
          behaves like not_change_object_value_by_update
            should not change `klass.find(object.id).send(key)`
          behaves like flash_alert_message
            should eq "クラスが更新できません."
      not owned object
        behaves like response_status_check
          response should be 302
        behaves like redirect_to
          should redirect to "/kurasus?edit_mode=true"
        behaves like flash_alert_message
          should eq "クラスが更新できません."

長くなったので今日はここまで